Нужно создать модель для предсказания коэффициента восстановления золота из золотосодержащей руды. Предоставлены данные с параметрами добычи и очистки.
Модель поможет оптимизировать производство, чтобы не запускать предприятие с убыточными характеристиками.
Данные находятся в трех файлах:
Данные индексируются датой и временем получения информации (признак date). Соседние по времени параметры часто похожи. Некоторые параметры недоступны, потому что замеряются и/или рассчитываются значительно позже. Из-за этого в тестовой выборке отсутствуют некоторые признаки, которые могут быть в обучающей. Также в тестовом наборе нет целевых признаков. Исходный датасет содержит обучающую и тестовую выборки со всеми признаками.
В нашем распоряжении сырые данные: их просто выгрузили из хранилища. Прежде чем приступить к построению модели, требуется проверить их на корректность согласно согласованной с заказчиком инструкции.
Наименование признаков строится по следующему правилу: [этап].[тип_параметра].[название_параметра]
Возможные обозначения этапов:
Возможные типы параметров:
Эффективность обогащения рассчитывается по формуле:
где:
$C$ — доля золота в концентрате после флотации/очистки;
$F$ — доля золота в сырье/концентрате до флотации/очистки;
$T$ — доля золота в отвальных хвостах после флотации/очистки.
Для прогноза коэффициента нужно найти долю золота в концентратах и хвостах. Причём важен не только финальный продукт, но и черновой концентрат.
Нужно спрогнозировать сразу две величины:
эффективность обогащения чернового концентрата rougher.output.recovery;
эффективность обогащения финального концентрата final.output.recovery.
Шаг 1. Загрузка и подготовка данных.
Шаг 2. Анализ данных.
Шаг 3. Написание функции для вычисления итоговой метрики. Построение моделей.
Шаг 4. Тестирование выбранной модели.
Шаг 5. Общий вывод.
#Загрузка необходимых библиотек:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import shap
from sklearn.metrics import make_scorer
from sklearn.metrics import mean_absolute_error
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.dummy import DummyRegressor
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from matplotlib.colors import LinearSegmentedColormap
#Сохраняем данные в датафреймы
df_train = pd.read_csv('C:/Users/ratus/DATA_SCIENCE/итоговые проекты/7/gold_industry_train.csv')
df_test = pd.read_csv('C:/Users/ratus/DATA_SCIENCE/итоговые проекты/7/gold_industry_test.csv')
df_full = pd.read_csv('C:/Users/ratus/DATA_SCIENCE/итоговые проекты/7/gold_industry_full.csv')
#Общая информация по обучающему датафрейму
#и несколько строк
df_train.info()
print (display(df_train.sample(5)))
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14579 entries, 0 to 14578 Data columns (total 87 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 14579 non-null object 1 rougher.input.feed_au 14579 non-null float64 2 rougher.input.feed_ag 14579 non-null float64 3 rougher.input.feed_pb 14507 non-null float64 4 rougher.input.feed_sol 14502 non-null float64 5 rougher.input.feed_rate 14572 non-null float64 6 rougher.input.feed_size 14478 non-null float64 7 rougher.input.floatbank10_sulfate 14548 non-null float64 8 rougher.input.floatbank10_xanthate 14572 non-null float64 9 rougher.state.floatbank10_a_air 14579 non-null float64 10 rougher.state.floatbank10_a_level 14579 non-null float64 11 rougher.state.floatbank10_b_air 14579 non-null float64 12 rougher.state.floatbank10_b_level 14579 non-null float64 13 rougher.state.floatbank10_c_air 14579 non-null float64 14 rougher.state.floatbank10_c_level 14579 non-null float64 15 rougher.state.floatbank10_d_air 14579 non-null float64 16 rougher.state.floatbank10_d_level 14579 non-null float64 17 rougher.state.floatbank10_e_air 14150 non-null float64 18 rougher.state.floatbank10_e_level 14579 non-null float64 19 rougher.state.floatbank10_f_air 14579 non-null float64 20 rougher.state.floatbank10_f_level 14579 non-null float64 21 rougher.input.floatbank11_sulfate 14543 non-null float64 22 rougher.input.floatbank11_xanthate 14172 non-null float64 23 rougher.calculation.sulfate_to_au_concentrate 14578 non-null float64 24 rougher.calculation.floatbank10_sulfate_to_au_feed 14578 non-null float64 25 rougher.calculation.floatbank11_sulfate_to_au_feed 14578 non-null float64 26 rougher.calculation.au_pb_ratio 14579 non-null float64 27 rougher.output.concentrate_au 14579 non-null float64 28 rougher.output.concentrate_ag 14579 non-null float64 29 rougher.output.concentrate_pb 14579 non-null float64 30 rougher.output.concentrate_sol 14561 non-null float64 31 rougher.output.recovery 14579 non-null float64 32 rougher.output.tail_au 14579 non-null float64 33 rougher.output.tail_ag 14578 non-null float64 34 rougher.output.tail_pb 14579 non-null float64 35 rougher.output.tail_sol 14579 non-null float64 36 primary_cleaner.input.sulfate 14556 non-null float64 37 primary_cleaner.input.depressant 14551 non-null float64 38 primary_cleaner.input.feed_size 14579 non-null float64 39 primary_cleaner.input.xanthate 14518 non-null float64 40 primary_cleaner.state.floatbank8_a_air 14576 non-null float64 41 primary_cleaner.state.floatbank8_a_level 14579 non-null float64 42 primary_cleaner.state.floatbank8_b_air 14576 non-null float64 43 primary_cleaner.state.floatbank8_b_level 14579 non-null float64 44 primary_cleaner.state.floatbank8_c_air 14579 non-null float64 45 primary_cleaner.state.floatbank8_c_level 14579 non-null float64 46 primary_cleaner.state.floatbank8_d_air 14578 non-null float64 47 primary_cleaner.state.floatbank8_d_level 14579 non-null float64 48 primary_cleaner.output.concentrate_au 14579 non-null float64 49 primary_cleaner.output.concentrate_ag 14579 non-null float64 50 primary_cleaner.output.concentrate_pb 14491 non-null float64 51 primary_cleaner.output.concentrate_sol 14314 non-null float64 52 primary_cleaner.output.tail_au 14579 non-null float64 53 primary_cleaner.output.tail_ag 14575 non-null float64 54 primary_cleaner.output.tail_pb 14573 non-null float64 55 primary_cleaner.output.tail_sol 14534 non-null float64 56 secondary_cleaner.state.floatbank2_a_air 14485 non-null float64 57 secondary_cleaner.state.floatbank2_a_level 14579 non-null float64 58 secondary_cleaner.state.floatbank2_b_air 14557 non-null float64 59 secondary_cleaner.state.floatbank2_b_level 14579 non-null float64 60 secondary_cleaner.state.floatbank3_a_air 14567 non-null float64 61 secondary_cleaner.state.floatbank3_a_level 14579 non-null float64 62 secondary_cleaner.state.floatbank3_b_air 14579 non-null float64 63 secondary_cleaner.state.floatbank3_b_level 14579 non-null float64 64 secondary_cleaner.state.floatbank4_a_air 14574 non-null float64 65 secondary_cleaner.state.floatbank4_a_level 14579 non-null float64 66 secondary_cleaner.state.floatbank4_b_air 14579 non-null float64 67 secondary_cleaner.state.floatbank4_b_level 14579 non-null float64 68 secondary_cleaner.state.floatbank5_a_air 14579 non-null float64 69 secondary_cleaner.state.floatbank5_a_level 14579 non-null float64 70 secondary_cleaner.state.floatbank5_b_air 14579 non-null float64 71 secondary_cleaner.state.floatbank5_b_level 14579 non-null float64 72 secondary_cleaner.state.floatbank6_a_air 14578 non-null float64 73 secondary_cleaner.state.floatbank6_a_level 14579 non-null float64 74 secondary_cleaner.output.tail_au 14579 non-null float64 75 secondary_cleaner.output.tail_ag 14578 non-null float64 76 secondary_cleaner.output.tail_pb 14575 non-null float64 77 secondary_cleaner.output.tail_sol 13659 non-null float64 78 final.output.concentrate_au 14579 non-null float64 79 final.output.concentrate_ag 14578 non-null float64 80 final.output.concentrate_pb 14578 non-null float64 81 final.output.concentrate_sol 14387 non-null float64 82 final.output.recovery 14579 non-null float64 83 final.output.tail_au 14579 non-null float64 84 final.output.tail_ag 14578 non-null float64 85 final.output.tail_pb 14504 non-null float64 86 final.output.tail_sol 14574 non-null float64 dtypes: float64(86), object(1) memory usage: 9.7+ MB
| date | rougher.input.feed_au | rougher.input.feed_ag | rougher.input.feed_pb | rougher.input.feed_sol | rougher.input.feed_rate | rougher.input.feed_size | rougher.input.floatbank10_sulfate | rougher.input.floatbank10_xanthate | rougher.state.floatbank10_a_air | ... | secondary_cleaner.output.tail_sol | final.output.concentrate_au | final.output.concentrate_ag | final.output.concentrate_pb | final.output.concentrate_sol | final.output.recovery | final.output.tail_au | final.output.tail_ag | final.output.tail_pb | final.output.tail_sol | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5910 | 2016-11-01 07:59:59 | 9.724082 | 10.382902 | 3.743033 | 39.084566 | 456.685191 | 46.068685 | 14.000177 | 5.998800 | 1000.435458 | ... | 12.041054 | 42.042444 | 6.963553 | 9.520725 | 14.008483 | 76.544204 | 2.771535 | 11.383462 | 3.018809 | 11.805815 |
| 2884 | 2016-06-09 08:59:59 | 8.077164 | 10.119045 | 2.206084 | 42.627917 | 478.135083 | 59.338296 | 13.003109 | 6.299449 | 998.392896 | ... | 11.045197 | 47.300888 | 4.925678 | 7.221204 | 13.190683 | 74.690745 | 2.343118 | 10.713390 | 2.058738 | 11.853321 |
| 13075 | 2017-10-04 03:59:59 | 6.745043 | 8.650523 | 3.285432 | 39.912213 | 577.413116 | 114.835466 | 7.928873 | 7.601904 | 1306.297298 | ... | 1.875735 | 47.388683 | 4.583551 | 9.984815 | 8.583462 | 72.561407 | 2.063905 | 10.095471 | 1.998406 | 9.243621 |
| 483 | 2016-02-06 14:00:00 | 6.975766 | 7.815967 | 2.721600 | 37.307483 | 564.329288 | 58.628890 | 12.938873 | 7.403678 | 999.693515 | ... | 10.155367 | 42.103671 | 6.098068 | 10.391891 | 3.732341 | 68.023210 | 2.513952 | 10.742393 | 1.492146 | 17.044335 |
| 14405 | 2017-12-01 23:59:59 | 9.657002 | 12.113081 | 5.405596 | 28.431578 | 508.700942 | 63.680452 | 15.252470 | 6.785144 | 1297.818998 | ... | NaN | 1.063992 | 0.289059 | 0.332391 | 0.291099 | 100.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
5 rows × 87 columns
None
#Общая информация по тестовому датафрейму
#и несколько строк
df_test.info()
print (display(df_test.sample(5)))
<class 'pandas.core.frame.DataFrame'> RangeIndex: 4860 entries, 0 to 4859 Data columns (total 53 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 4860 non-null object 1 rougher.input.feed_au 4860 non-null float64 2 rougher.input.feed_ag 4860 non-null float64 3 rougher.input.feed_pb 4832 non-null float64 4 rougher.input.feed_sol 4838 non-null float64 5 rougher.input.feed_rate 4856 non-null float64 6 rougher.input.feed_size 4816 non-null float64 7 rougher.input.floatbank10_sulfate 4857 non-null float64 8 rougher.input.floatbank10_xanthate 4859 non-null float64 9 rougher.state.floatbank10_a_air 4859 non-null float64 10 rougher.state.floatbank10_a_level 4859 non-null float64 11 rougher.state.floatbank10_b_air 4859 non-null float64 12 rougher.state.floatbank10_b_level 4859 non-null float64 13 rougher.state.floatbank10_c_air 4859 non-null float64 14 rougher.state.floatbank10_c_level 4859 non-null float64 15 rougher.state.floatbank10_d_air 4860 non-null float64 16 rougher.state.floatbank10_d_level 4860 non-null float64 17 rougher.state.floatbank10_e_air 4853 non-null float64 18 rougher.state.floatbank10_e_level 4860 non-null float64 19 rougher.state.floatbank10_f_air 4860 non-null float64 20 rougher.state.floatbank10_f_level 4860 non-null float64 21 rougher.input.floatbank11_sulfate 4852 non-null float64 22 rougher.input.floatbank11_xanthate 4814 non-null float64 23 primary_cleaner.input.sulfate 4859 non-null float64 24 primary_cleaner.input.depressant 4851 non-null float64 25 primary_cleaner.input.feed_size 4860 non-null float64 26 primary_cleaner.input.xanthate 4817 non-null float64 27 primary_cleaner.state.floatbank8_a_air 4859 non-null float64 28 primary_cleaner.state.floatbank8_a_level 4859 non-null float64 29 primary_cleaner.state.floatbank8_b_air 4859 non-null float64 30 primary_cleaner.state.floatbank8_b_level 4859 non-null float64 31 primary_cleaner.state.floatbank8_c_air 4858 non-null float64 32 primary_cleaner.state.floatbank8_c_level 4859 non-null float64 33 primary_cleaner.state.floatbank8_d_air 4858 non-null float64 34 primary_cleaner.state.floatbank8_d_level 4859 non-null float64 35 secondary_cleaner.state.floatbank2_a_air 4734 non-null float64 36 secondary_cleaner.state.floatbank2_a_level 4859 non-null float64 37 secondary_cleaner.state.floatbank2_b_air 4859 non-null float64 38 secondary_cleaner.state.floatbank2_b_level 4859 non-null float64 39 secondary_cleaner.state.floatbank3_a_air 4859 non-null float64 40 secondary_cleaner.state.floatbank3_a_level 4859 non-null float64 41 secondary_cleaner.state.floatbank3_b_air 4859 non-null float64 42 secondary_cleaner.state.floatbank3_b_level 4859 non-null float64 43 secondary_cleaner.state.floatbank4_a_air 4859 non-null float64 44 secondary_cleaner.state.floatbank4_a_level 4859 non-null float64 45 secondary_cleaner.state.floatbank4_b_air 4859 non-null float64 46 secondary_cleaner.state.floatbank4_b_level 4859 non-null float64 47 secondary_cleaner.state.floatbank5_a_air 4859 non-null float64 48 secondary_cleaner.state.floatbank5_a_level 4859 non-null float64 49 secondary_cleaner.state.floatbank5_b_air 4859 non-null float64 50 secondary_cleaner.state.floatbank5_b_level 4859 non-null float64 51 secondary_cleaner.state.floatbank6_a_air 4859 non-null float64 52 secondary_cleaner.state.floatbank6_a_level 4859 non-null float64 dtypes: float64(52), object(1) memory usage: 2.0+ MB
| date | rougher.input.feed_au | rougher.input.feed_ag | rougher.input.feed_pb | rougher.input.feed_sol | rougher.input.feed_rate | rougher.input.feed_size | rougher.input.floatbank10_sulfate | rougher.input.floatbank10_xanthate | rougher.state.floatbank10_a_air | ... | secondary_cleaner.state.floatbank4_a_air | secondary_cleaner.state.floatbank4_a_level | secondary_cleaner.state.floatbank4_b_air | secondary_cleaner.state.floatbank4_b_level | secondary_cleaner.state.floatbank5_a_air | secondary_cleaner.state.floatbank5_a_level | secondary_cleaner.state.floatbank5_b_air | secondary_cleaner.state.floatbank5_b_level | secondary_cleaner.state.floatbank6_a_air | secondary_cleaner.state.floatbank6_a_level | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 4013 | 2018-07-03 02:59:59 | 8.432230 | 10.285341 | 2.544935 | 38.281301 | 519.189379 | 52.811335 | 11.998801 | 5.991807 | 998.985815 | ... | 15.043026 | -399.884739 | 5.030000 | -399.285948 | 10.004478 | -401.789780 | 4.914104 | -399.635096 | 23.000363 | -500.067986 |
| 432 | 2017-12-29 23:59:59 | 7.735872 | 7.833009 | 3.551006 | 39.905848 | 573.211977 | 76.586118 | 11.010039 | 6.105982 | 1199.509474 | ... | 30.042706 | -494.343661 | 21.968409 | -496.514717 | 25.015701 | -500.755373 | 23.001236 | -500.294089 | 25.021266 | -500.189949 |
| 4195 | 2018-07-11 17:59:59 | 5.782546 | 6.904235 | 2.510215 | 27.171296 | 416.272247 | 46.636943 | 11.001742 | 4.298051 | 1000.721076 | ... | 9.001526 | -548.317784 | 6.960951 | -549.929750 | 7.945181 | -550.778904 | 6.030613 | -549.982531 | 18.019536 | -549.691175 |
| 471 | 2017-12-31 14:59:59 | 5.834999 | 5.554842 | 3.879765 | 43.432120 | 552.585895 | 74.308772 | 8.624969 | 5.903379 | 1207.677160 | ... | 30.044379 | -500.789891 | 22.049197 | -500.494616 | 25.020861 | -499.810634 | 22.993582 | -499.961007 | 24.984409 | -499.894076 |
| 4494 | 2018-07-31 06:59:59 | 6.599017 | 7.138930 | 1.799329 | 33.137979 | 530.143595 | 53.095511 | 9.216710 | 6.543349 | 998.482042 | ... | 12.062538 | -495.366459 | 11.984371 | -498.668948 | 12.021770 | -497.904602 | 9.922863 | -499.424789 | 19.991247 | -498.427915 |
5 rows × 53 columns
None
#Общая информация по полному датафрейму
#и несколько строк
df_full.info()
print (display(df_full.sample(5)))
<class 'pandas.core.frame.DataFrame'> RangeIndex: 19439 entries, 0 to 19438 Data columns (total 87 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 19439 non-null object 1 rougher.input.feed_au 19439 non-null float64 2 rougher.input.feed_ag 19439 non-null float64 3 rougher.input.feed_pb 19339 non-null float64 4 rougher.input.feed_sol 19340 non-null float64 5 rougher.input.feed_rate 19428 non-null float64 6 rougher.input.feed_size 19294 non-null float64 7 rougher.input.floatbank10_sulfate 19405 non-null float64 8 rougher.input.floatbank10_xanthate 19431 non-null float64 9 rougher.state.floatbank10_a_air 19438 non-null float64 10 rougher.state.floatbank10_a_level 19438 non-null float64 11 rougher.state.floatbank10_b_air 19438 non-null float64 12 rougher.state.floatbank10_b_level 19438 non-null float64 13 rougher.state.floatbank10_c_air 19438 non-null float64 14 rougher.state.floatbank10_c_level 19438 non-null float64 15 rougher.state.floatbank10_d_air 19439 non-null float64 16 rougher.state.floatbank10_d_level 19439 non-null float64 17 rougher.state.floatbank10_e_air 19003 non-null float64 18 rougher.state.floatbank10_e_level 19439 non-null float64 19 rougher.state.floatbank10_f_air 19439 non-null float64 20 rougher.state.floatbank10_f_level 19439 non-null float64 21 rougher.input.floatbank11_sulfate 19395 non-null float64 22 rougher.input.floatbank11_xanthate 18986 non-null float64 23 rougher.calculation.sulfate_to_au_concentrate 19437 non-null float64 24 rougher.calculation.floatbank10_sulfate_to_au_feed 19437 non-null float64 25 rougher.calculation.floatbank11_sulfate_to_au_feed 19437 non-null float64 26 rougher.calculation.au_pb_ratio 19439 non-null float64 27 rougher.output.concentrate_au 19439 non-null float64 28 rougher.output.concentrate_ag 19439 non-null float64 29 rougher.output.concentrate_pb 19439 non-null float64 30 rougher.output.concentrate_sol 19416 non-null float64 31 rougher.output.recovery 19439 non-null float64 32 rougher.output.tail_au 19439 non-null float64 33 rougher.output.tail_ag 19438 non-null float64 34 rougher.output.tail_pb 19439 non-null float64 35 rougher.output.tail_sol 19439 non-null float64 36 primary_cleaner.input.sulfate 19415 non-null float64 37 primary_cleaner.input.depressant 19402 non-null float64 38 primary_cleaner.input.feed_size 19439 non-null float64 39 primary_cleaner.input.xanthate 19335 non-null float64 40 primary_cleaner.state.floatbank8_a_air 19435 non-null float64 41 primary_cleaner.state.floatbank8_a_level 19438 non-null float64 42 primary_cleaner.state.floatbank8_b_air 19435 non-null float64 43 primary_cleaner.state.floatbank8_b_level 19438 non-null float64 44 primary_cleaner.state.floatbank8_c_air 19437 non-null float64 45 primary_cleaner.state.floatbank8_c_level 19438 non-null float64 46 primary_cleaner.state.floatbank8_d_air 19436 non-null float64 47 primary_cleaner.state.floatbank8_d_level 19438 non-null float64 48 primary_cleaner.output.concentrate_au 19439 non-null float64 49 primary_cleaner.output.concentrate_ag 19439 non-null float64 50 primary_cleaner.output.concentrate_pb 19323 non-null float64 51 primary_cleaner.output.concentrate_sol 19069 non-null float64 52 primary_cleaner.output.tail_au 19439 non-null float64 53 primary_cleaner.output.tail_ag 19435 non-null float64 54 primary_cleaner.output.tail_pb 19418 non-null float64 55 primary_cleaner.output.tail_sol 19377 non-null float64 56 secondary_cleaner.state.floatbank2_a_air 19219 non-null float64 57 secondary_cleaner.state.floatbank2_a_level 19438 non-null float64 58 secondary_cleaner.state.floatbank2_b_air 19416 non-null float64 59 secondary_cleaner.state.floatbank2_b_level 19438 non-null float64 60 secondary_cleaner.state.floatbank3_a_air 19426 non-null float64 61 secondary_cleaner.state.floatbank3_a_level 19438 non-null float64 62 secondary_cleaner.state.floatbank3_b_air 19438 non-null float64 63 secondary_cleaner.state.floatbank3_b_level 19438 non-null float64 64 secondary_cleaner.state.floatbank4_a_air 19433 non-null float64 65 secondary_cleaner.state.floatbank4_a_level 19438 non-null float64 66 secondary_cleaner.state.floatbank4_b_air 19438 non-null float64 67 secondary_cleaner.state.floatbank4_b_level 19438 non-null float64 68 secondary_cleaner.state.floatbank5_a_air 19438 non-null float64 69 secondary_cleaner.state.floatbank5_a_level 19438 non-null float64 70 secondary_cleaner.state.floatbank5_b_air 19438 non-null float64 71 secondary_cleaner.state.floatbank5_b_level 19438 non-null float64 72 secondary_cleaner.state.floatbank6_a_air 19437 non-null float64 73 secondary_cleaner.state.floatbank6_a_level 19438 non-null float64 74 secondary_cleaner.output.tail_au 19439 non-null float64 75 secondary_cleaner.output.tail_ag 19437 non-null float64 76 secondary_cleaner.output.tail_pb 19427 non-null float64 77 secondary_cleaner.output.tail_sol 17691 non-null float64 78 final.output.concentrate_au 19439 non-null float64 79 final.output.concentrate_ag 19438 non-null float64 80 final.output.concentrate_pb 19438 non-null float64 81 final.output.concentrate_sol 19228 non-null float64 82 final.output.recovery 19439 non-null float64 83 final.output.tail_au 19439 non-null float64 84 final.output.tail_ag 19438 non-null float64 85 final.output.tail_pb 19338 non-null float64 86 final.output.tail_sol 19433 non-null float64 dtypes: float64(86), object(1) memory usage: 12.9+ MB
| date | rougher.input.feed_au | rougher.input.feed_ag | rougher.input.feed_pb | rougher.input.feed_sol | rougher.input.feed_rate | rougher.input.feed_size | rougher.input.floatbank10_sulfate | rougher.input.floatbank10_xanthate | rougher.state.floatbank10_a_air | ... | secondary_cleaner.output.tail_sol | final.output.concentrate_au | final.output.concentrate_ag | final.output.concentrate_pb | final.output.concentrate_sol | final.output.recovery | final.output.tail_au | final.output.tail_ag | final.output.tail_pb | final.output.tail_sol | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2543 | 2016-05-25 19:59:59 | 6.767325 | 9.029444 | 1.646339 | 40.305792 | 582.072706 | 52.168355 | 11.999247 | 5.697436 | 996.143889 | ... | 13.579404 | 44.386532 | 6.308949 | 7.777812 | 10.060313 | 75.822142 | 1.850064 | 9.119501 | 1.234776 | 13.633746 |
| 10188 | 2017-05-24 07:59:59 | 7.982723 | 7.848322 | 3.456345 | 43.806594 | 610.341722 | 63.282054 | 17.999972 | 5.990782 | 1002.493491 | ... | 0.971201 | 45.570961 | 5.026765 | 10.885644 | 6.674334 | 79.789593 | 1.875473 | 6.961824 | 2.162162 | 12.914274 |
| 2987 | 2016-06-14 00:59:59 | 8.027234 | 7.901779 | 3.399599 | 33.544106 | 416.125833 | 50.082269 | 9.998171 | 5.998030 | 986.528072 | ... | 7.434477 | 41.533187 | 7.175354 | 9.956534 | 11.668659 | 58.350052 | 3.768309 | 8.471305 | 2.551703 | 9.986052 |
| 5509 | 2016-10-15 12:59:59 | 9.412145 | 9.985785 | 3.858534 | 34.622963 | 393.927215 | 43.475561 | 10.001728 | 5.498627 | 997.983727 | ... | 9.921439 | 47.008838 | 4.146553 | 8.731920 | 13.710393 | 67.498592 | 3.537106 | 10.836328 | 3.645316 | 9.190656 |
| 8432 | 2017-02-25 07:59:59 | 6.836434 | 7.427734 | 3.213347 | 34.808280 | 430.939816 | 49.427467 | 15.998316 | 4.898914 | 1000.148823 | ... | 2.861656 | 46.320606 | 4.018887 | 10.952900 | 8.181732 | 63.436320 | 2.757857 | 8.306170 | 2.981936 | 10.026701 |
5 rows × 87 columns
None
В обучающем датасете у нас содержится информация о 14579 наблюдениях по 87 признакам.
В тестовом датасете у нас содержится информация о 4860 наблюдениях по 53 признакам.
В общем датасете у нас содержится информация о 19439 наблюдениях по 87 признакам.
Признаков в тестовой выборке значительно меньше, скорее всего, часть признаков мы не можем знать в момент оценки и предсказаний.
Во всех датафреймах заметно что есть пропуски в данных, надо будет далее посмотреть подробнее.
snapecase в названиях соблюден, все маленькими буквами, однако необходиом заменить все точки в названии на нижние подчеркивания.
С типами данных считаем что все в порядке, только тип данных для признака data надо будет заменить на datetime. Возможно этот признак перед моделированием можно будет удалить, так как по логике он не должен оказывать влияния на коэффициент восстановления золота из руды.
def parser(columns_name):
"""
Функция для замены точек на нижние подчеркивания
Получает названия признаков
Возвращает словарик, где в качестве ключей имена старых
признаков, а в качестве значений новые имена с нижними
подчеркиваниями
"""
#Создаем словарик, в котором ключами будут имена старых
#признаков,а значениями имена новых
list_of_keys = columns_name
new_columns_name = {k: 0 for k in list_of_keys}
try:
#Идем по списку наших имен и заменяем везде
#точки на нижние подчеркивания
#обновленное имя добавляем в качестве значения
#в словарик new_columns_name
for column_name in columns_name:
new_columns_name[column_name] = column_name.replace('.', '_')
except:
print ('error in parser function')
return(new_columns_name)
Переименуем для обучающего датафрейма:
#получим новые названия признаков
df_train_new_columns = parser(df_train.columns)
#Переименовываем
df_train = df_train.rename(columns = df_train_new_columns )
#Проверяем
df_train.columns
Index(['date', 'rougher_input_feed_au', 'rougher_input_feed_ag',
'rougher_input_feed_pb', 'rougher_input_feed_sol',
'rougher_input_feed_rate', 'rougher_input_feed_size',
'rougher_input_floatbank10_sulfate',
'rougher_input_floatbank10_xanthate', 'rougher_state_floatbank10_a_air',
'rougher_state_floatbank10_a_level', 'rougher_state_floatbank10_b_air',
'rougher_state_floatbank10_b_level', 'rougher_state_floatbank10_c_air',
'rougher_state_floatbank10_c_level', 'rougher_state_floatbank10_d_air',
'rougher_state_floatbank10_d_level', 'rougher_state_floatbank10_e_air',
'rougher_state_floatbank10_e_level', 'rougher_state_floatbank10_f_air',
'rougher_state_floatbank10_f_level',
'rougher_input_floatbank11_sulfate',
'rougher_input_floatbank11_xanthate',
'rougher_calculation_sulfate_to_au_concentrate',
'rougher_calculation_floatbank10_sulfate_to_au_feed',
'rougher_calculation_floatbank11_sulfate_to_au_feed',
'rougher_calculation_au_pb_ratio', 'rougher_output_concentrate_au',
'rougher_output_concentrate_ag', 'rougher_output_concentrate_pb',
'rougher_output_concentrate_sol', 'rougher_output_recovery',
'rougher_output_tail_au', 'rougher_output_tail_ag',
'rougher_output_tail_pb', 'rougher_output_tail_sol',
'primary_cleaner_input_sulfate', 'primary_cleaner_input_depressant',
'primary_cleaner_input_feed_size', 'primary_cleaner_input_xanthate',
'primary_cleaner_state_floatbank8_a_air',
'primary_cleaner_state_floatbank8_a_level',
'primary_cleaner_state_floatbank8_b_air',
'primary_cleaner_state_floatbank8_b_level',
'primary_cleaner_state_floatbank8_c_air',
'primary_cleaner_state_floatbank8_c_level',
'primary_cleaner_state_floatbank8_d_air',
'primary_cleaner_state_floatbank8_d_level',
'primary_cleaner_output_concentrate_au',
'primary_cleaner_output_concentrate_ag',
'primary_cleaner_output_concentrate_pb',
'primary_cleaner_output_concentrate_sol',
'primary_cleaner_output_tail_au', 'primary_cleaner_output_tail_ag',
'primary_cleaner_output_tail_pb', 'primary_cleaner_output_tail_sol',
'secondary_cleaner_state_floatbank2_a_air',
'secondary_cleaner_state_floatbank2_a_level',
'secondary_cleaner_state_floatbank2_b_air',
'secondary_cleaner_state_floatbank2_b_level',
'secondary_cleaner_state_floatbank3_a_air',
'secondary_cleaner_state_floatbank3_a_level',
'secondary_cleaner_state_floatbank3_b_air',
'secondary_cleaner_state_floatbank3_b_level',
'secondary_cleaner_state_floatbank4_a_air',
'secondary_cleaner_state_floatbank4_a_level',
'secondary_cleaner_state_floatbank4_b_air',
'secondary_cleaner_state_floatbank4_b_level',
'secondary_cleaner_state_floatbank5_a_air',
'secondary_cleaner_state_floatbank5_a_level',
'secondary_cleaner_state_floatbank5_b_air',
'secondary_cleaner_state_floatbank5_b_level',
'secondary_cleaner_state_floatbank6_a_air',
'secondary_cleaner_state_floatbank6_a_level',
'secondary_cleaner_output_tail_au', 'secondary_cleaner_output_tail_ag',
'secondary_cleaner_output_tail_pb', 'secondary_cleaner_output_tail_sol',
'final_output_concentrate_au', 'final_output_concentrate_ag',
'final_output_concentrate_pb', 'final_output_concentrate_sol',
'final_output_recovery', 'final_output_tail_au', 'final_output_tail_ag',
'final_output_tail_pb', 'final_output_tail_sol'],
dtype='object')
Переименуем признаки в тестовом датафрейме:
#получим новые названия признаков
df_test_new_columns = parser(df_test.columns)
#Переименовываем
df_test = df_test.rename(columns = df_test_new_columns )
#Проверяем
df_test.columns
Index(['date', 'rougher_input_feed_au', 'rougher_input_feed_ag',
'rougher_input_feed_pb', 'rougher_input_feed_sol',
'rougher_input_feed_rate', 'rougher_input_feed_size',
'rougher_input_floatbank10_sulfate',
'rougher_input_floatbank10_xanthate', 'rougher_state_floatbank10_a_air',
'rougher_state_floatbank10_a_level', 'rougher_state_floatbank10_b_air',
'rougher_state_floatbank10_b_level', 'rougher_state_floatbank10_c_air',
'rougher_state_floatbank10_c_level', 'rougher_state_floatbank10_d_air',
'rougher_state_floatbank10_d_level', 'rougher_state_floatbank10_e_air',
'rougher_state_floatbank10_e_level', 'rougher_state_floatbank10_f_air',
'rougher_state_floatbank10_f_level',
'rougher_input_floatbank11_sulfate',
'rougher_input_floatbank11_xanthate', 'primary_cleaner_input_sulfate',
'primary_cleaner_input_depressant', 'primary_cleaner_input_feed_size',
'primary_cleaner_input_xanthate',
'primary_cleaner_state_floatbank8_a_air',
'primary_cleaner_state_floatbank8_a_level',
'primary_cleaner_state_floatbank8_b_air',
'primary_cleaner_state_floatbank8_b_level',
'primary_cleaner_state_floatbank8_c_air',
'primary_cleaner_state_floatbank8_c_level',
'primary_cleaner_state_floatbank8_d_air',
'primary_cleaner_state_floatbank8_d_level',
'secondary_cleaner_state_floatbank2_a_air',
'secondary_cleaner_state_floatbank2_a_level',
'secondary_cleaner_state_floatbank2_b_air',
'secondary_cleaner_state_floatbank2_b_level',
'secondary_cleaner_state_floatbank3_a_air',
'secondary_cleaner_state_floatbank3_a_level',
'secondary_cleaner_state_floatbank3_b_air',
'secondary_cleaner_state_floatbank3_b_level',
'secondary_cleaner_state_floatbank4_a_air',
'secondary_cleaner_state_floatbank4_a_level',
'secondary_cleaner_state_floatbank4_b_air',
'secondary_cleaner_state_floatbank4_b_level',
'secondary_cleaner_state_floatbank5_a_air',
'secondary_cleaner_state_floatbank5_a_level',
'secondary_cleaner_state_floatbank5_b_air',
'secondary_cleaner_state_floatbank5_b_level',
'secondary_cleaner_state_floatbank6_a_air',
'secondary_cleaner_state_floatbank6_a_level'],
dtype='object')
И в общем датафрейме
#получим новые названия признаков
df_full_new_columns = parser(df_full.columns)
#Переименовываем
df_full = df_full.rename(columns = df_full_new_columns )
#Проверяем
df_full.columns
Index(['date', 'rougher_input_feed_au', 'rougher_input_feed_ag',
'rougher_input_feed_pb', 'rougher_input_feed_sol',
'rougher_input_feed_rate', 'rougher_input_feed_size',
'rougher_input_floatbank10_sulfate',
'rougher_input_floatbank10_xanthate', 'rougher_state_floatbank10_a_air',
'rougher_state_floatbank10_a_level', 'rougher_state_floatbank10_b_air',
'rougher_state_floatbank10_b_level', 'rougher_state_floatbank10_c_air',
'rougher_state_floatbank10_c_level', 'rougher_state_floatbank10_d_air',
'rougher_state_floatbank10_d_level', 'rougher_state_floatbank10_e_air',
'rougher_state_floatbank10_e_level', 'rougher_state_floatbank10_f_air',
'rougher_state_floatbank10_f_level',
'rougher_input_floatbank11_sulfate',
'rougher_input_floatbank11_xanthate',
'rougher_calculation_sulfate_to_au_concentrate',
'rougher_calculation_floatbank10_sulfate_to_au_feed',
'rougher_calculation_floatbank11_sulfate_to_au_feed',
'rougher_calculation_au_pb_ratio', 'rougher_output_concentrate_au',
'rougher_output_concentrate_ag', 'rougher_output_concentrate_pb',
'rougher_output_concentrate_sol', 'rougher_output_recovery',
'rougher_output_tail_au', 'rougher_output_tail_ag',
'rougher_output_tail_pb', 'rougher_output_tail_sol',
'primary_cleaner_input_sulfate', 'primary_cleaner_input_depressant',
'primary_cleaner_input_feed_size', 'primary_cleaner_input_xanthate',
'primary_cleaner_state_floatbank8_a_air',
'primary_cleaner_state_floatbank8_a_level',
'primary_cleaner_state_floatbank8_b_air',
'primary_cleaner_state_floatbank8_b_level',
'primary_cleaner_state_floatbank8_c_air',
'primary_cleaner_state_floatbank8_c_level',
'primary_cleaner_state_floatbank8_d_air',
'primary_cleaner_state_floatbank8_d_level',
'primary_cleaner_output_concentrate_au',
'primary_cleaner_output_concentrate_ag',
'primary_cleaner_output_concentrate_pb',
'primary_cleaner_output_concentrate_sol',
'primary_cleaner_output_tail_au', 'primary_cleaner_output_tail_ag',
'primary_cleaner_output_tail_pb', 'primary_cleaner_output_tail_sol',
'secondary_cleaner_state_floatbank2_a_air',
'secondary_cleaner_state_floatbank2_a_level',
'secondary_cleaner_state_floatbank2_b_air',
'secondary_cleaner_state_floatbank2_b_level',
'secondary_cleaner_state_floatbank3_a_air',
'secondary_cleaner_state_floatbank3_a_level',
'secondary_cleaner_state_floatbank3_b_air',
'secondary_cleaner_state_floatbank3_b_level',
'secondary_cleaner_state_floatbank4_a_air',
'secondary_cleaner_state_floatbank4_a_level',
'secondary_cleaner_state_floatbank4_b_air',
'secondary_cleaner_state_floatbank4_b_level',
'secondary_cleaner_state_floatbank5_a_air',
'secondary_cleaner_state_floatbank5_a_level',
'secondary_cleaner_state_floatbank5_b_air',
'secondary_cleaner_state_floatbank5_b_level',
'secondary_cleaner_state_floatbank6_a_air',
'secondary_cleaner_state_floatbank6_a_level',
'secondary_cleaner_output_tail_au', 'secondary_cleaner_output_tail_ag',
'secondary_cleaner_output_tail_pb', 'secondary_cleaner_output_tail_sol',
'final_output_concentrate_au', 'final_output_concentrate_ag',
'final_output_concentrate_pb', 'final_output_concentrate_sol',
'final_output_recovery', 'final_output_tail_au', 'final_output_tail_ag',
'final_output_tail_pb', 'final_output_tail_sol'],
dtype='object')
#Посмотрим каких признаков нет в тестовой выборке
df_train.columns.difference(df_test.columns)
Index(['final_output_concentrate_ag', 'final_output_concentrate_au',
'final_output_concentrate_pb', 'final_output_concentrate_sol',
'final_output_recovery', 'final_output_tail_ag', 'final_output_tail_au',
'final_output_tail_pb', 'final_output_tail_sol',
'primary_cleaner_output_concentrate_ag',
'primary_cleaner_output_concentrate_au',
'primary_cleaner_output_concentrate_pb',
'primary_cleaner_output_concentrate_sol',
'primary_cleaner_output_tail_ag', 'primary_cleaner_output_tail_au',
'primary_cleaner_output_tail_pb', 'primary_cleaner_output_tail_sol',
'rougher_calculation_au_pb_ratio',
'rougher_calculation_floatbank10_sulfate_to_au_feed',
'rougher_calculation_floatbank11_sulfate_to_au_feed',
'rougher_calculation_sulfate_to_au_concentrate',
'rougher_output_concentrate_ag', 'rougher_output_concentrate_au',
'rougher_output_concentrate_pb', 'rougher_output_concentrate_sol',
'rougher_output_recovery', 'rougher_output_tail_ag',
'rougher_output_tail_au', 'rougher_output_tail_pb',
'rougher_output_tail_sol', 'secondary_cleaner_output_tail_ag',
'secondary_cleaner_output_tail_au', 'secondary_cleaner_output_tail_pb',
'secondary_cleaner_output_tail_sol'],
dtype='object')
В тестовом датафрейме не хватает итоговых характеристик в концентрате и в хвостах после каждого этапа: флотации, первого и второго этапа очистки и финального, есть данные только "до". Поэтому исключим данные признаки из обущающего набора перед моделированием.
В том числе в тестовой выборке нет целевых признаков final.output.recovery и rougher.output.recovery
Добавим в тестовый датасет целевые признаки из общего датафрейма
print (df_test.shape)
(4860, 53)
#Вытащим нужные колонки из общего датасета во вспомогательный
data = df_full[['final_output_recovery','rougher_output_recovery','date']]
#Добавим в тестовую таблицу полученные колонки
df_test = df_test.merge(data, on='date', how='inner')
#Проверяем
print (df_test.columns)
print (df_test.shape)
Index(['date', 'rougher_input_feed_au', 'rougher_input_feed_ag',
'rougher_input_feed_pb', 'rougher_input_feed_sol',
'rougher_input_feed_rate', 'rougher_input_feed_size',
'rougher_input_floatbank10_sulfate',
'rougher_input_floatbank10_xanthate', 'rougher_state_floatbank10_a_air',
'rougher_state_floatbank10_a_level', 'rougher_state_floatbank10_b_air',
'rougher_state_floatbank10_b_level', 'rougher_state_floatbank10_c_air',
'rougher_state_floatbank10_c_level', 'rougher_state_floatbank10_d_air',
'rougher_state_floatbank10_d_level', 'rougher_state_floatbank10_e_air',
'rougher_state_floatbank10_e_level', 'rougher_state_floatbank10_f_air',
'rougher_state_floatbank10_f_level',
'rougher_input_floatbank11_sulfate',
'rougher_input_floatbank11_xanthate', 'primary_cleaner_input_sulfate',
'primary_cleaner_input_depressant', 'primary_cleaner_input_feed_size',
'primary_cleaner_input_xanthate',
'primary_cleaner_state_floatbank8_a_air',
'primary_cleaner_state_floatbank8_a_level',
'primary_cleaner_state_floatbank8_b_air',
'primary_cleaner_state_floatbank8_b_level',
'primary_cleaner_state_floatbank8_c_air',
'primary_cleaner_state_floatbank8_c_level',
'primary_cleaner_state_floatbank8_d_air',
'primary_cleaner_state_floatbank8_d_level',
'secondary_cleaner_state_floatbank2_a_air',
'secondary_cleaner_state_floatbank2_a_level',
'secondary_cleaner_state_floatbank2_b_air',
'secondary_cleaner_state_floatbank2_b_level',
'secondary_cleaner_state_floatbank3_a_air',
'secondary_cleaner_state_floatbank3_a_level',
'secondary_cleaner_state_floatbank3_b_air',
'secondary_cleaner_state_floatbank3_b_level',
'secondary_cleaner_state_floatbank4_a_air',
'secondary_cleaner_state_floatbank4_a_level',
'secondary_cleaner_state_floatbank4_b_air',
'secondary_cleaner_state_floatbank4_b_level',
'secondary_cleaner_state_floatbank5_a_air',
'secondary_cleaner_state_floatbank5_a_level',
'secondary_cleaner_state_floatbank5_b_air',
'secondary_cleaner_state_floatbank5_b_level',
'secondary_cleaner_state_floatbank6_a_air',
'secondary_cleaner_state_floatbank6_a_level', 'final_output_recovery',
'rougher_output_recovery'],
dtype='object')
(4860, 55)
# Общая гистограмма для всех числовых столбцов
df_train.hist(figsize=(25, 50), layout=(18,5),
color = '#616161', edgecolor = 'black')
plt.suptitle('Гистограммы для числовых признаков датафрейма:', fontsize=14);
В обучающем датафрейме похоже на выбросы или наличие аномальных данных в следующих признаках (из тех, что будут участвовать в моделировании):
Попробуем почистить аномалии и выбросы, так как чистка данных особенно для линейных моделей (а мы будем пробовать в том числе линейную регрессию) может улучшить результат.
Для всех признаков, в которых есть аномальные данные обрежем по 0.4 процентиля слева и справа (заменим на пустые значения).
#Признаки для обрезки аномальных значений и выбросов
columns = ['rougher_input_feed_size',
'rougher_state_floatbank10_a_air',
'rougher_state_floatbank10_f_air',
'rougher_state_floatbank10_e_air',]
#перебирая признаки, находим для каждого процентили для обрезки
#и обрезаем данные
for column in columns:
procentile_996 = df_train[column].quantile(.996)
procentile_004 = df_train[column].quantile(.004)
print(column, 'пустых значений до ', df_train[column].isna().sum())
isna_before = df_train[column].isna().sum()
df_train.loc[df_train[column]<procentile_004, column] = None
df_train.loc[df_train[column]>procentile_996, column] = None
print(column, 'пустых значений после ',df_train[column].isna().sum())
rougher_input_feed_size пустых значений до 101 rougher_input_feed_size пустых значений после 217 rougher_state_floatbank10_a_air пустых значений до 0 rougher_state_floatbank10_a_air пустых значений после 118 rougher_state_floatbank10_f_air пустых значений до 0 rougher_state_floatbank10_f_air пустых значений после 118 rougher_state_floatbank10_e_air пустых значений до 429 rougher_state_floatbank10_e_air пустых значений после 543
# Общая гистограмма для этих обновленных столбцов
df_train[columns].hist(figsize=(16, 3), layout=(1,4),
color = '#616161', edgecolor = 'black')
plt.suptitle('Гистограммы для обновленных столбцов:', fontsize=14);
Так, ну уже вроде получше, посмотрим теперь данные на наличие аномальностей и выбросов в тестовом датафрейме:
# Общая гистограмма для всех числовых столбцов
df_test.hist(figsize=(25, 35), layout=(11,5),
color = '#cb4154', edgecolor = 'black')
plt.suptitle('Гистограммы для числовых признаков датафрейма:', fontsize=14);
В тестовом датафрейме выбросов и аномальных данных можно сказать нет. Посмотрим на общий фрейм
# Общая гистограмма для всех числовых столбцов
df_full.hist(figsize=(25, 50), layout=(18,5),
color = '#5d89ba', edgecolor = 'black')
plt.suptitle('Гистограммы для числовых признаков датафрейма:', fontsize=14);
В общем датафрейме похоже на выбросы или наличие аномальных данных в следующих признаках:
Общий датафрейм оставим пока так. Если понадобится тут избавится от аномалий, вернемся.
Проверим, что эффективность обогащения рассчитана правильно для признака rougher_output_recovery.
#Вычислим эффективность обагащения после этапа флотации
c = df_train['rougher_output_concentrate_au']
f = df_train['rougher_input_feed_au']
t = df_train['rougher_output_tail_au']
check_r_o_recovery = c*(f-t)*100/(f*(c-t))
# Найдем среднее абсолютное отклонение
#между имеющимся признаком и посчитанным нами:
print ('MAE =', mean_absolute_error(df_train['rougher_output_recovery'],check_r_o_recovery))
MAE = 1.1181885454685472e-14
Ответы совпадают с точностью до 13 знака после запятой, что говорит о том, что эффективность обогащения рассчитана верно.
Выведем для обучающего датафрейма столбцы, в которых есть пропуски с указанием процента пропущенных значений
def table_of_missing_data(data):
"""
Возвращает таблицу с количеством
и долей пропусков в датафрейме"""
d = (
data.isna().sum().rename("кол-во пропущенных строк").to_frame()
.join(round((data.isna().mean()*100),2).rename("процент пропусков").to_frame())
.sort_values(by="кол-во пропущенных строк", ascending=False)
# Удаляем данные о признаках, где количество пропусков = 0
.loc[lambda x: x["кол-во пропущенных строк"] > 0]
)
return d
table_of_missing_data(df_train)
| кол-во пропущенных строк | процент пропусков | |
|---|---|---|
| secondary_cleaner_output_tail_sol | 920 | 6.31 |
| rougher_state_floatbank10_e_air | 543 | 3.72 |
| rougher_input_floatbank11_xanthate | 407 | 2.79 |
| primary_cleaner_output_concentrate_sol | 265 | 1.82 |
| rougher_input_feed_size | 217 | 1.49 |
| final_output_concentrate_sol | 192 | 1.32 |
| rougher_state_floatbank10_a_air | 118 | 0.81 |
| rougher_state_floatbank10_f_air | 118 | 0.81 |
| secondary_cleaner_state_floatbank2_a_air | 94 | 0.64 |
| primary_cleaner_output_concentrate_pb | 88 | 0.60 |
| rougher_input_feed_sol | 77 | 0.53 |
| final_output_tail_pb | 75 | 0.51 |
| rougher_input_feed_pb | 72 | 0.49 |
| primary_cleaner_input_xanthate | 61 | 0.42 |
| primary_cleaner_output_tail_sol | 45 | 0.31 |
| rougher_input_floatbank11_sulfate | 36 | 0.25 |
| rougher_input_floatbank10_sulfate | 31 | 0.21 |
| primary_cleaner_input_depressant | 28 | 0.19 |
| primary_cleaner_input_sulfate | 23 | 0.16 |
| secondary_cleaner_state_floatbank2_b_air | 22 | 0.15 |
| rougher_output_concentrate_sol | 18 | 0.12 |
| secondary_cleaner_state_floatbank3_a_air | 12 | 0.08 |
| rougher_input_floatbank10_xanthate | 7 | 0.05 |
| rougher_input_feed_rate | 7 | 0.05 |
| primary_cleaner_output_tail_pb | 6 | 0.04 |
| secondary_cleaner_state_floatbank4_a_air | 5 | 0.03 |
| final_output_tail_sol | 5 | 0.03 |
| secondary_cleaner_output_tail_pb | 4 | 0.03 |
| primary_cleaner_output_tail_ag | 4 | 0.03 |
| primary_cleaner_state_floatbank8_b_air | 3 | 0.02 |
| primary_cleaner_state_floatbank8_a_air | 3 | 0.02 |
| primary_cleaner_state_floatbank8_d_air | 1 | 0.01 |
| final_output_concentrate_ag | 1 | 0.01 |
| secondary_cleaner_output_tail_ag | 1 | 0.01 |
| final_output_concentrate_pb | 1 | 0.01 |
| rougher_calculation_sulfate_to_au_concentrate | 1 | 0.01 |
| rougher_calculation_floatbank10_sulfate_to_au_feed | 1 | 0.01 |
| rougher_output_tail_ag | 1 | 0.01 |
| final_output_tail_ag | 1 | 0.01 |
| rougher_calculation_floatbank11_sulfate_to_au_feed | 1 | 0.01 |
| secondary_cleaner_state_floatbank6_a_air | 1 | 0.01 |
Больше всего пропусков (6.3%) в одном из признаков состояния на черновом этапе rougher_state_floatbank10_e_air
Посмотрим сумму пропущенных значений по всем столбцам (с учетом пересечений т.е. того что пропуск для наблюдения может быть одновременно для нескольких признаков)
index = set(
item
for column in df_train.columns
for item in df_train.loc[df_train[column].isna()].index
)
len(index)
2546
Это 10,7% от всего датафрейма..немало( Наверное все же стоит подумать как заменить пропуски.
Учитывая что наши данные индексируются датой и временем получения информации (признак date), соседние по времени параметры часто похожи. Поэтому заменим пропуски с помощью метода fill()
df_train = df_train.ffill(axis=0)
#Проверим
table_of_missing_data(df_train)
| кол-во пропущенных строк | процент пропусков |
|---|
Выведем для тестового датафрейма столбцы, в которых есть пропуски с указанием процента пропущенных значений:
table_of_missing_data(df_test)
| кол-во пропущенных строк | процент пропусков | |
|---|---|---|
| secondary_cleaner_state_floatbank2_a_air | 126 | 2.59 |
| rougher_input_floatbank11_xanthate | 46 | 0.95 |
| rougher_input_feed_size | 44 | 0.91 |
| primary_cleaner_input_xanthate | 43 | 0.88 |
| rougher_input_feed_pb | 28 | 0.58 |
| rougher_input_feed_sol | 22 | 0.45 |
| primary_cleaner_input_depressant | 9 | 0.19 |
| rougher_input_floatbank11_sulfate | 8 | 0.16 |
| rougher_state_floatbank10_e_air | 7 | 0.14 |
| rougher_input_feed_rate | 4 | 0.08 |
| rougher_input_floatbank10_sulfate | 3 | 0.06 |
| primary_cleaner_state_floatbank8_c_air | 2 | 0.04 |
| primary_cleaner_state_floatbank8_d_air | 2 | 0.04 |
| primary_cleaner_state_floatbank8_a_level | 1 | 0.02 |
| primary_cleaner_state_floatbank8_b_air | 1 | 0.02 |
| primary_cleaner_state_floatbank8_b_level | 1 | 0.02 |
| primary_cleaner_state_floatbank8_c_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank4_a_air | 1 | 0.02 |
| primary_cleaner_state_floatbank8_d_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank2_a_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank4_b_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank2_b_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank3_a_air | 1 | 0.02 |
| secondary_cleaner_state_floatbank3_a_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank3_b_air | 1 | 0.02 |
| secondary_cleaner_state_floatbank3_b_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank2_b_air | 1 | 0.02 |
| primary_cleaner_state_floatbank8_a_air | 1 | 0.02 |
| secondary_cleaner_state_floatbank5_a_air | 1 | 0.02 |
| primary_cleaner_input_sulfate | 1 | 0.02 |
| rougher_input_floatbank10_xanthate | 1 | 0.02 |
| rougher_state_floatbank10_a_air | 1 | 0.02 |
| rougher_state_floatbank10_a_level | 1 | 0.02 |
| rougher_state_floatbank10_b_air | 1 | 0.02 |
| rougher_state_floatbank10_b_level | 1 | 0.02 |
| rougher_state_floatbank10_c_air | 1 | 0.02 |
| rougher_state_floatbank10_c_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank6_a_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank6_a_air | 1 | 0.02 |
| secondary_cleaner_state_floatbank5_b_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank5_b_air | 1 | 0.02 |
| secondary_cleaner_state_floatbank5_a_level | 1 | 0.02 |
| secondary_cleaner_state_floatbank4_b_air | 1 | 0.02 |
| secondary_cleaner_state_floatbank4_a_level | 1 | 0.02 |
В тестовом датафрейме уже не так много пропусков. Также обработаем их методом fill()
df_test = df_test.ffill(axis=0)
#Проверим
table_of_missing_data(df_test)
| кол-во пропущенных строк | процент пропусков |
|---|
Выведем для общего датафрейма столбцы, в которых есть пропуски с указанием процента пропущенных значений:
table_of_missing_data(df_full)
| кол-во пропущенных строк | процент пропусков | |
|---|---|---|
| secondary_cleaner_output_tail_sol | 1748 | 8.99 |
| rougher_input_floatbank11_xanthate | 453 | 2.33 |
| rougher_state_floatbank10_e_air | 436 | 2.24 |
| primary_cleaner_output_concentrate_sol | 370 | 1.90 |
| secondary_cleaner_state_floatbank2_a_air | 220 | 1.13 |
| ... | ... | ... |
| rougher_state_floatbank10_c_level | 1 | 0.01 |
| rougher_state_floatbank10_c_air | 1 | 0.01 |
| rougher_output_tail_ag | 1 | 0.01 |
| rougher_state_floatbank10_b_air | 1 | 0.01 |
| rougher_state_floatbank10_b_level | 1 | 0.01 |
63 rows × 2 columns
В общем датафрейме пропусков больше всего. Но общий датафрейм пока не будем трогать.
#Ищем дубликаты по всему датафрейму
print (df_train.duplicated().sum())
print (df_test.duplicated().sum())
print (df_full.duplicated().sum())
0 0 0
Во всех трех датафреймах дубликатов строк не обнаружено.
#Взглянем еще раз на текущий формат
df_train['date']
0 2016-01-15 00:00:00
1 2016-01-15 01:00:00
2 2016-01-15 02:00:00
3 2016-01-15 03:00:00
4 2016-01-15 04:00:00
...
14574 2017-12-09 09:59:59
14575 2017-12-09 10:59:59
14576 2017-12-09 11:59:59
14577 2017-12-09 12:59:59
14578 2017-12-09 13:59:59
Name: date, Length: 14579, dtype: object
#Изменяем тип
df_train['date'] = pd.to_datetime(
df_train['date'], format = '%Y-%m-%dT%H:%M:%S')
df_test['date'] = pd.to_datetime(
df_test['date'], format = '%Y-%m-%dT%H:%M:%S')
df_full['date'] = pd.to_datetime(
df_full['date'], format = '%Y-%m-%dT%H:%M:%S')
#Перебирая необходимые металлы строим графики для разных этапов
for element in ['au', 'ag', 'pb']:
fig = plt.figure(figsize=(16, 5))
ax = fig.add_subplot(111)
ax.set_title(
f'Изменение концентрации {element} на различных этапах')
ax.set_xlabel('концентрация')
ax.set_ylabel('кол-во наблюдений')
df_full[f'rougher_input_feed_{element}'].plot(
kind = 'hist', label = 'в сырье',
bins = 100, ax=ax, alpha = 0.7,
color = '#616161', edgecolor = 'black');
df_full[f'rougher_output_concentrate_{element}'].plot(
kind = 'hist', label = 'в черновом концентрате',
bins = 100, ax=ax, alpha = 0.7,
color = '#303F9F', edgecolor = 'black');
df_full[f'primary_cleaner_output_concentrate_{element}'].plot(
kind = 'hist', label = 'в концентрате после первой очистки',
bins = 100, ax=ax, alpha = 0.7,
color = '#cb4154', edgecolor = 'black');
df_full[f'final_output_concentrate_{element}'].plot(
kind = 'hist', label = 'в финальном концентрате',
bins = 100, ax=ax, alpha = 0.7,
color = '#FBC02D', edgecolor = 'black');
plt.legend();
Видно как концентрация золота существенно увеличивается после каждого этапа.
Концентрация серебра увеличивается в черновом концентрате до самых своих больших значений, и далее падает.
Концентрация свинца увеличивается в черновом концентрате и после первой очистки, и следующие манипуляции не сильно изменяют эту концентрацию, в финале она остается примерно такой же как и после первой очистки.
На графиках заметны еще некоторые аномалии - значения концентраций около нуля. Они конечно уменьшаются на каждом этапе, но особенно они выглядят аномальными и странными для финального концентрата, что похоже на ошибки измерений.
Посмотрим есть ли в обучающем датафрейме те данные, для которых финальная концентрация нулевая на примере золота:
df_train.query('final_output_concentrate_au == 0')[['rougher_input_feed_au',
'rougher_output_concentrate_au',
'primary_cleaner_output_concentrate_au',
'final_output_concentrate_au']]
| rougher_input_feed_au | rougher_output_concentrate_au | primary_cleaner_output_concentrate_au | final_output_concentrate_au | |
|---|---|---|---|---|
| 516 | 5.598971 | 15.935190 | 26.238846 | 0.0 |
| 943 | 7.786147 | 18.682162 | 34.564456 | 0.0 |
| 944 | 7.499248 | 18.231121 | 34.399450 | 0.0 |
| 945 | 7.032278 | 18.190655 | 34.303756 | 0.0 |
| 946 | 7.021635 | 18.194323 | 33.956378 | 0.0 |
| ... | ... | ... | ... | ... |
| 11553 | 11.604771 | 19.442973 | 34.015777 | 0.0 |
| 11554 | 11.763586 | 17.784973 | 27.937329 | 0.0 |
| 11754 | 7.345464 | 20.047747 | 2.635256 | 0.0 |
| 11755 | 7.687805 | 22.632637 | 32.702605 | 0.0 |
| 11856 | 6.009365 | 13.743304 | 8.144001 | 0.0 |
98 rows × 4 columns
Для золота таких наблюдений 98, и выглядят они весьма странно, концентрация после предыдущих этапов есть, а в финале нулевая. Посмотрим для других металлов:
df_train.query('final_output_concentrate_ag == 0')[['rougher_input_feed_ag',
'rougher_output_concentrate_ag',
'primary_cleaner_output_concentrate_ag',
'final_output_concentrate_ag']]
| rougher_input_feed_ag | rougher_output_concentrate_ag | primary_cleaner_output_concentrate_ag | final_output_concentrate_ag | |
|---|---|---|---|---|
| 516 | 7.407737 | 11.803589 | 8.986696 | 0.0 |
| 943 | 8.098125 | 10.640168 | 8.701237 | 0.0 |
| 944 | 7.832467 | 10.415491 | 8.697952 | 0.0 |
| 945 | 7.600277 | 10.197899 | 8.921656 | 0.0 |
| 946 | 7.436860 | 9.968103 | 9.239378 | 0.0 |
| ... | ... | ... | ... | ... |
| 11553 | 12.464094 | 14.424928 | 8.415798 | 0.0 |
| 11554 | 12.726401 | 14.028955 | 11.517548 | 0.0 |
| 11754 | 9.398085 | 12.043514 | 0.634975 | 0.0 |
| 11755 | 9.812272 | 14.639309 | 8.735761 | 0.0 |
| 11856 | 6.503673 | 9.522966 | 1.834584 | 0.0 |
98 rows × 4 columns
Видно что для серебра это те же самые наблюдения
df_train.query('final_output_concentrate_pb == 0')[['rougher_input_feed_pb',
'rougher_output_concentrate_pb',
'primary_cleaner_output_concentrate_pb',
'final_output_concentrate_pb']]
| rougher_input_feed_pb | rougher_output_concentrate_pb | primary_cleaner_output_concentrate_pb | final_output_concentrate_pb | |
|---|---|---|---|---|
| 516 | 1.934178 | 7.143475 | 6.530607 | 0.0 |
| 943 | 2.588661 | 7.565854 | 7.234158 | 0.0 |
| 944 | 2.435125 | 7.367937 | 7.119325 | 0.0 |
| 945 | 2.265148 | 7.364725 | 6.637383 | 0.0 |
| 946 | 2.265435 | 7.282737 | 6.249012 | 0.0 |
| ... | ... | ... | ... | ... |
| 11553 | 5.393215 | 8.120457 | 10.210094 | 0.0 |
| 11554 | 5.630816 | 8.029784 | 11.462232 | 0.0 |
| 11754 | 2.653756 | 9.540977 | 0.090132 | 0.0 |
| 11755 | 2.738460 | 6.588981 | 7.265347 | 0.0 |
| 11856 | 2.522602 | 4.906476 | 2.338291 | 0.0 |
98 rows × 4 columns
И для свинца также. Может можно было подумать и заморочиться как-то заменить их исходя из предыдущих или последующих по времени наблюдений, но их число не велико, поэтому просто удалим эти 98 наблюдений из датафрейма:
#Запоминаем индексы этих наблюдений
index = df_train.query('final_output_concentrate_au == 0').index
#Удаляем
df_train.drop(index, inplace = True)
#Обновим индексацию датафрейма
df_train.reset_index(drop=True, inplace= True)
#Проверяем
df_train.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14481 entries, 0 to 14480 Data columns (total 87 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 14481 non-null datetime64[ns] 1 rougher_input_feed_au 14481 non-null float64 2 rougher_input_feed_ag 14481 non-null float64 3 rougher_input_feed_pb 14481 non-null float64 4 rougher_input_feed_sol 14481 non-null float64 5 rougher_input_feed_rate 14481 non-null float64 6 rougher_input_feed_size 14481 non-null float64 7 rougher_input_floatbank10_sulfate 14481 non-null float64 8 rougher_input_floatbank10_xanthate 14481 non-null float64 9 rougher_state_floatbank10_a_air 14481 non-null float64 10 rougher_state_floatbank10_a_level 14481 non-null float64 11 rougher_state_floatbank10_b_air 14481 non-null float64 12 rougher_state_floatbank10_b_level 14481 non-null float64 13 rougher_state_floatbank10_c_air 14481 non-null float64 14 rougher_state_floatbank10_c_level 14481 non-null float64 15 rougher_state_floatbank10_d_air 14481 non-null float64 16 rougher_state_floatbank10_d_level 14481 non-null float64 17 rougher_state_floatbank10_e_air 14481 non-null float64 18 rougher_state_floatbank10_e_level 14481 non-null float64 19 rougher_state_floatbank10_f_air 14481 non-null float64 20 rougher_state_floatbank10_f_level 14481 non-null float64 21 rougher_input_floatbank11_sulfate 14481 non-null float64 22 rougher_input_floatbank11_xanthate 14481 non-null float64 23 rougher_calculation_sulfate_to_au_concentrate 14481 non-null float64 24 rougher_calculation_floatbank10_sulfate_to_au_feed 14481 non-null float64 25 rougher_calculation_floatbank11_sulfate_to_au_feed 14481 non-null float64 26 rougher_calculation_au_pb_ratio 14481 non-null float64 27 rougher_output_concentrate_au 14481 non-null float64 28 rougher_output_concentrate_ag 14481 non-null float64 29 rougher_output_concentrate_pb 14481 non-null float64 30 rougher_output_concentrate_sol 14481 non-null float64 31 rougher_output_recovery 14481 non-null float64 32 rougher_output_tail_au 14481 non-null float64 33 rougher_output_tail_ag 14481 non-null float64 34 rougher_output_tail_pb 14481 non-null float64 35 rougher_output_tail_sol 14481 non-null float64 36 primary_cleaner_input_sulfate 14481 non-null float64 37 primary_cleaner_input_depressant 14481 non-null float64 38 primary_cleaner_input_feed_size 14481 non-null float64 39 primary_cleaner_input_xanthate 14481 non-null float64 40 primary_cleaner_state_floatbank8_a_air 14481 non-null float64 41 primary_cleaner_state_floatbank8_a_level 14481 non-null float64 42 primary_cleaner_state_floatbank8_b_air 14481 non-null float64 43 primary_cleaner_state_floatbank8_b_level 14481 non-null float64 44 primary_cleaner_state_floatbank8_c_air 14481 non-null float64 45 primary_cleaner_state_floatbank8_c_level 14481 non-null float64 46 primary_cleaner_state_floatbank8_d_air 14481 non-null float64 47 primary_cleaner_state_floatbank8_d_level 14481 non-null float64 48 primary_cleaner_output_concentrate_au 14481 non-null float64 49 primary_cleaner_output_concentrate_ag 14481 non-null float64 50 primary_cleaner_output_concentrate_pb 14481 non-null float64 51 primary_cleaner_output_concentrate_sol 14481 non-null float64 52 primary_cleaner_output_tail_au 14481 non-null float64 53 primary_cleaner_output_tail_ag 14481 non-null float64 54 primary_cleaner_output_tail_pb 14481 non-null float64 55 primary_cleaner_output_tail_sol 14481 non-null float64 56 secondary_cleaner_state_floatbank2_a_air 14481 non-null float64 57 secondary_cleaner_state_floatbank2_a_level 14481 non-null float64 58 secondary_cleaner_state_floatbank2_b_air 14481 non-null float64 59 secondary_cleaner_state_floatbank2_b_level 14481 non-null float64 60 secondary_cleaner_state_floatbank3_a_air 14481 non-null float64 61 secondary_cleaner_state_floatbank3_a_level 14481 non-null float64 62 secondary_cleaner_state_floatbank3_b_air 14481 non-null float64 63 secondary_cleaner_state_floatbank3_b_level 14481 non-null float64 64 secondary_cleaner_state_floatbank4_a_air 14481 non-null float64 65 secondary_cleaner_state_floatbank4_a_level 14481 non-null float64 66 secondary_cleaner_state_floatbank4_b_air 14481 non-null float64 67 secondary_cleaner_state_floatbank4_b_level 14481 non-null float64 68 secondary_cleaner_state_floatbank5_a_air 14481 non-null float64 69 secondary_cleaner_state_floatbank5_a_level 14481 non-null float64 70 secondary_cleaner_state_floatbank5_b_air 14481 non-null float64 71 secondary_cleaner_state_floatbank5_b_level 14481 non-null float64 72 secondary_cleaner_state_floatbank6_a_air 14481 non-null float64 73 secondary_cleaner_state_floatbank6_a_level 14481 non-null float64 74 secondary_cleaner_output_tail_au 14481 non-null float64 75 secondary_cleaner_output_tail_ag 14481 non-null float64 76 secondary_cleaner_output_tail_pb 14481 non-null float64 77 secondary_cleaner_output_tail_sol 14481 non-null float64 78 final_output_concentrate_au 14481 non-null float64 79 final_output_concentrate_ag 14481 non-null float64 80 final_output_concentrate_pb 14481 non-null float64 81 final_output_concentrate_sol 14481 non-null float64 82 final_output_recovery 14481 non-null float64 83 final_output_tail_au 14481 non-null float64 84 final_output_tail_ag 14481 non-null float64 85 final_output_tail_pb 14481 non-null float64 86 final_output_tail_sol 14481 non-null float64 dtypes: datetime64[ns](1), float64(86) memory usage: 9.6 MB
#Формируем столбец с суммарной концентрацией в сырье
sum_r_input_concentrate = df_full['rougher_input_feed_au'] +\
df_full['rougher_input_feed_ag'] + df_full['rougher_input_feed_pb']
#Формируем столбец с суммарной концентрацией в черновом концентрате
sum_r_output_concentrate = df_full['rougher_output_concentrate_au'] + \
df_full['rougher_output_concentrate_ag'] + df_full['rougher_output_concentrate_pb']
#Формируем столбец с суммарной концентрацией после первой очистки
sum_pr_output_concentrate = df_full['primary_cleaner_output_concentrate_au'] +\
df_full['primary_cleaner_output_concentrate_ag'] + \
df_full['primary_cleaner_output_concentrate_pb']
#Формируем столбец с суммарной концентрацией в финальном концентрате
sum_final_concentrate = df_full['final_output_concentrate_au'] + \
df_full['final_output_concentrate_ag'] + df_full['final_output_concentrate_pb']
# строим графики для разных этапов
fig = plt.figure(figsize=(16, 5))
ax = fig.add_subplot(111)
ax.set_title( 'Суммарная концентрация металлов на различных этапах')
ax.set_xlabel('концентрация')
ax.set_ylabel('кол-во наблюдений')
sum_r_input_concentrate.plot(kind = 'hist', label = 'в сырье',
bins = 100, ax=ax, alpha = 0.7,
color = '#616161', edgecolor = 'black')
sum_r_output_concentrate.plot(kind = 'hist', label = 'в черновом концентрате',
bins = 100, ax=ax, alpha = 0.7,
color = '#303F9F', edgecolor = 'black');
sum_pr_output_concentrate.plot(kind = 'hist', label = 'в концентрате после первой очистки',
bins = 100, ax=ax, alpha = 0.7,
color = '#cb4154', edgecolor = 'black');
sum_final_concentrate.plot(kind = 'hist', label = 'в финальном концентрате',
bins = 100, ax=ax, alpha = 0.7,
color = '#FBC02D', edgecolor = 'black');
plt.legend();
На графике видно, что общая концентрация металлов растет после каждого следующего этапа, а разброс уменьшается, что соответствует здравому смыслу.
Также заметно, что есть нулевые и околонулевые значения суммарных концентрацией. Скорее всего это связано со сбоем измерительного оборудования. Удалим такие наблюдения из обучающих данных:
#Формируем столбец с суммарной концентрацией в сырье по обущающим данным
sum_r_input_concentrate_train = df_train['rougher_input_feed_au'] +\
df_train['rougher_input_feed_ag'] + df_train['rougher_input_feed_pb']
#столбец с суммарной концентрацией в черновом концентрате
sum_r_output_concentrate_train = df_train['rougher_output_concentrate_au'] + \
df_train['rougher_output_concentrate_ag'] + df_train['rougher_output_concentrate_pb']
#столбец с суммарной концентрацией после первой очистки
sum_pr_output_concentrate_train = df_train['primary_cleaner_output_concentrate_au'] +\
df_train['primary_cleaner_output_concentrate_ag'] + \
df_train['primary_cleaner_output_concentrate_pb']
#столбец с суммарной концентрацией в финальном концентрате
sum_final_concentrate_train = df_train['final_output_concentrate_au'] + \
df_train['final_output_concentrate_ag'] + df_train['final_output_concentrate_pb']
#Запоминаем индексы таких наблюдений
index_for_delete = []
index_for_delete.extend(sum_r_input_concentrate_train.loc[ lambda x : x <= 2].index)
index_for_delete.extend(sum_r_output_concentrate_train.loc[ lambda x : x <= 4].index)
index_for_delete.extend(sum_pr_output_concentrate_train.loc[ lambda x : x <= 5].index)
index_for_delete.extend(sum_final_concentrate_train.loc[ lambda x : x <= 6].index)
#Удаляем
df_train.drop(index_for_delete, inplace = True)
#Обновим индексацию датафрейма
df_train.reset_index(drop=True, inplace= True)
#Проверяем
df_train.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 13862 entries, 0 to 13861 Data columns (total 87 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 13862 non-null datetime64[ns] 1 rougher_input_feed_au 13862 non-null float64 2 rougher_input_feed_ag 13862 non-null float64 3 rougher_input_feed_pb 13862 non-null float64 4 rougher_input_feed_sol 13862 non-null float64 5 rougher_input_feed_rate 13862 non-null float64 6 rougher_input_feed_size 13862 non-null float64 7 rougher_input_floatbank10_sulfate 13862 non-null float64 8 rougher_input_floatbank10_xanthate 13862 non-null float64 9 rougher_state_floatbank10_a_air 13862 non-null float64 10 rougher_state_floatbank10_a_level 13862 non-null float64 11 rougher_state_floatbank10_b_air 13862 non-null float64 12 rougher_state_floatbank10_b_level 13862 non-null float64 13 rougher_state_floatbank10_c_air 13862 non-null float64 14 rougher_state_floatbank10_c_level 13862 non-null float64 15 rougher_state_floatbank10_d_air 13862 non-null float64 16 rougher_state_floatbank10_d_level 13862 non-null float64 17 rougher_state_floatbank10_e_air 13862 non-null float64 18 rougher_state_floatbank10_e_level 13862 non-null float64 19 rougher_state_floatbank10_f_air 13862 non-null float64 20 rougher_state_floatbank10_f_level 13862 non-null float64 21 rougher_input_floatbank11_sulfate 13862 non-null float64 22 rougher_input_floatbank11_xanthate 13862 non-null float64 23 rougher_calculation_sulfate_to_au_concentrate 13862 non-null float64 24 rougher_calculation_floatbank10_sulfate_to_au_feed 13862 non-null float64 25 rougher_calculation_floatbank11_sulfate_to_au_feed 13862 non-null float64 26 rougher_calculation_au_pb_ratio 13862 non-null float64 27 rougher_output_concentrate_au 13862 non-null float64 28 rougher_output_concentrate_ag 13862 non-null float64 29 rougher_output_concentrate_pb 13862 non-null float64 30 rougher_output_concentrate_sol 13862 non-null float64 31 rougher_output_recovery 13862 non-null float64 32 rougher_output_tail_au 13862 non-null float64 33 rougher_output_tail_ag 13862 non-null float64 34 rougher_output_tail_pb 13862 non-null float64 35 rougher_output_tail_sol 13862 non-null float64 36 primary_cleaner_input_sulfate 13862 non-null float64 37 primary_cleaner_input_depressant 13862 non-null float64 38 primary_cleaner_input_feed_size 13862 non-null float64 39 primary_cleaner_input_xanthate 13862 non-null float64 40 primary_cleaner_state_floatbank8_a_air 13862 non-null float64 41 primary_cleaner_state_floatbank8_a_level 13862 non-null float64 42 primary_cleaner_state_floatbank8_b_air 13862 non-null float64 43 primary_cleaner_state_floatbank8_b_level 13862 non-null float64 44 primary_cleaner_state_floatbank8_c_air 13862 non-null float64 45 primary_cleaner_state_floatbank8_c_level 13862 non-null float64 46 primary_cleaner_state_floatbank8_d_air 13862 non-null float64 47 primary_cleaner_state_floatbank8_d_level 13862 non-null float64 48 primary_cleaner_output_concentrate_au 13862 non-null float64 49 primary_cleaner_output_concentrate_ag 13862 non-null float64 50 primary_cleaner_output_concentrate_pb 13862 non-null float64 51 primary_cleaner_output_concentrate_sol 13862 non-null float64 52 primary_cleaner_output_tail_au 13862 non-null float64 53 primary_cleaner_output_tail_ag 13862 non-null float64 54 primary_cleaner_output_tail_pb 13862 non-null float64 55 primary_cleaner_output_tail_sol 13862 non-null float64 56 secondary_cleaner_state_floatbank2_a_air 13862 non-null float64 57 secondary_cleaner_state_floatbank2_a_level 13862 non-null float64 58 secondary_cleaner_state_floatbank2_b_air 13862 non-null float64 59 secondary_cleaner_state_floatbank2_b_level 13862 non-null float64 60 secondary_cleaner_state_floatbank3_a_air 13862 non-null float64 61 secondary_cleaner_state_floatbank3_a_level 13862 non-null float64 62 secondary_cleaner_state_floatbank3_b_air 13862 non-null float64 63 secondary_cleaner_state_floatbank3_b_level 13862 non-null float64 64 secondary_cleaner_state_floatbank4_a_air 13862 non-null float64 65 secondary_cleaner_state_floatbank4_a_level 13862 non-null float64 66 secondary_cleaner_state_floatbank4_b_air 13862 non-null float64 67 secondary_cleaner_state_floatbank4_b_level 13862 non-null float64 68 secondary_cleaner_state_floatbank5_a_air 13862 non-null float64 69 secondary_cleaner_state_floatbank5_a_level 13862 non-null float64 70 secondary_cleaner_state_floatbank5_b_air 13862 non-null float64 71 secondary_cleaner_state_floatbank5_b_level 13862 non-null float64 72 secondary_cleaner_state_floatbank6_a_air 13862 non-null float64 73 secondary_cleaner_state_floatbank6_a_level 13862 non-null float64 74 secondary_cleaner_output_tail_au 13862 non-null float64 75 secondary_cleaner_output_tail_ag 13862 non-null float64 76 secondary_cleaner_output_tail_pb 13862 non-null float64 77 secondary_cleaner_output_tail_sol 13862 non-null float64 78 final_output_concentrate_au 13862 non-null float64 79 final_output_concentrate_ag 13862 non-null float64 80 final_output_concentrate_pb 13862 non-null float64 81 final_output_concentrate_sol 13862 non-null float64 82 final_output_recovery 13862 non-null float64 83 final_output_tail_au 13862 non-null float64 84 final_output_tail_ag 13862 non-null float64 85 final_output_tail_pb 13862 non-null float64 86 final_output_tail_sol 13862 non-null float64 dtypes: datetime64[ns](1), float64(86) memory usage: 9.2 MB
Если распределения будут сильно отличаются друг от друга, оценка модели будет неправильной.
fig = plt.figure(figsize=(10, 5))
ax = fig.add_subplot(111)
ax.set_title(
f'Распределение размеров гранул исходного сырья')
ax.set_xlabel('размер гранул')
ax.set_ylabel('доля наблюдений')
df_train['rougher_input_feed_size'].plot(
kind = 'hist', label = 'в обучающей выборке',
bins = 110, alpha=0.8,
density = True, #нормализация
ax=ax)
df_test['rougher_input_feed_size'].plot(
kind = 'hist', label = 'в тестовой выборке',
bins = 110, alpha=0.5,
color = 'r', density = True,
ax=ax)
plt.legend();
Распределение размеров гранул исходного сырья очень похожи, для моделирования нас такая картина устраивает.
Для решения задачи будем использовать метрику качества — sMAPE (англ. Symmetric Mean Absolute Percentage Error, «симметричное среднее абсолютное процентное отклонение»). Она похожа на MAE, но выражается не в абсолютных величинах, а в относительных. Симметричная, потому что она одинаково учитывает масштаб и целевого признака, и предсказания.
Метрика sMAPE вычисляется следующим образом:
Напомним, что мы должны спрогнозировать две величины:
Итоговая метрика складывается из двух величин:
$$ Final\_sMAPE = 25\% \times sMAPE(rougher) + 75\% \times sMAPE(final) $$Напишем функцию для вычисления sMAPE:
def smape(true, predict):
"""
Функция для расчета метрики sMAPE
На вход принимает целевые значения и значения предсказаний
"""
smape = (1/len(true))*np.sum(
(np.abs(true - predict))/((np.abs(true)+np.abs(predict))/2))*100
return smape
def final_smape(rougher, final):
"""
Функция для расчета итоговой sMAPE
На вход принимает sMAPE чернового концентрата
и SMAPE финального
"""
final = 0.25*rougher+0.75*final
return final
smape_score = make_scorer(smape)
diff = df_train.columns.difference(df_test.columns)
diff
Index(['final_output_concentrate_ag', 'final_output_concentrate_au',
'final_output_concentrate_pb', 'final_output_concentrate_sol',
'final_output_tail_ag', 'final_output_tail_au', 'final_output_tail_pb',
'final_output_tail_sol', 'primary_cleaner_output_concentrate_ag',
'primary_cleaner_output_concentrate_au',
'primary_cleaner_output_concentrate_pb',
'primary_cleaner_output_concentrate_sol',
'primary_cleaner_output_tail_ag', 'primary_cleaner_output_tail_au',
'primary_cleaner_output_tail_pb', 'primary_cleaner_output_tail_sol',
'rougher_calculation_au_pb_ratio',
'rougher_calculation_floatbank10_sulfate_to_au_feed',
'rougher_calculation_floatbank11_sulfate_to_au_feed',
'rougher_calculation_sulfate_to_au_concentrate',
'rougher_output_concentrate_ag', 'rougher_output_concentrate_au',
'rougher_output_concentrate_pb', 'rougher_output_concentrate_sol',
'rougher_output_tail_ag', 'rougher_output_tail_au',
'rougher_output_tail_pb', 'rougher_output_tail_sol',
'secondary_cleaner_output_tail_ag', 'secondary_cleaner_output_tail_au',
'secondary_cleaner_output_tail_pb',
'secondary_cleaner_output_tail_sol'],
dtype='object')
df_train = df_train.drop(np.array(diff), axis=1)
#Проверяем
df_train.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 13862 entries, 0 to 13861 Data columns (total 55 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 date 13862 non-null datetime64[ns] 1 rougher_input_feed_au 13862 non-null float64 2 rougher_input_feed_ag 13862 non-null float64 3 rougher_input_feed_pb 13862 non-null float64 4 rougher_input_feed_sol 13862 non-null float64 5 rougher_input_feed_rate 13862 non-null float64 6 rougher_input_feed_size 13862 non-null float64 7 rougher_input_floatbank10_sulfate 13862 non-null float64 8 rougher_input_floatbank10_xanthate 13862 non-null float64 9 rougher_state_floatbank10_a_air 13862 non-null float64 10 rougher_state_floatbank10_a_level 13862 non-null float64 11 rougher_state_floatbank10_b_air 13862 non-null float64 12 rougher_state_floatbank10_b_level 13862 non-null float64 13 rougher_state_floatbank10_c_air 13862 non-null float64 14 rougher_state_floatbank10_c_level 13862 non-null float64 15 rougher_state_floatbank10_d_air 13862 non-null float64 16 rougher_state_floatbank10_d_level 13862 non-null float64 17 rougher_state_floatbank10_e_air 13862 non-null float64 18 rougher_state_floatbank10_e_level 13862 non-null float64 19 rougher_state_floatbank10_f_air 13862 non-null float64 20 rougher_state_floatbank10_f_level 13862 non-null float64 21 rougher_input_floatbank11_sulfate 13862 non-null float64 22 rougher_input_floatbank11_xanthate 13862 non-null float64 23 rougher_output_recovery 13862 non-null float64 24 primary_cleaner_input_sulfate 13862 non-null float64 25 primary_cleaner_input_depressant 13862 non-null float64 26 primary_cleaner_input_feed_size 13862 non-null float64 27 primary_cleaner_input_xanthate 13862 non-null float64 28 primary_cleaner_state_floatbank8_a_air 13862 non-null float64 29 primary_cleaner_state_floatbank8_a_level 13862 non-null float64 30 primary_cleaner_state_floatbank8_b_air 13862 non-null float64 31 primary_cleaner_state_floatbank8_b_level 13862 non-null float64 32 primary_cleaner_state_floatbank8_c_air 13862 non-null float64 33 primary_cleaner_state_floatbank8_c_level 13862 non-null float64 34 primary_cleaner_state_floatbank8_d_air 13862 non-null float64 35 primary_cleaner_state_floatbank8_d_level 13862 non-null float64 36 secondary_cleaner_state_floatbank2_a_air 13862 non-null float64 37 secondary_cleaner_state_floatbank2_a_level 13862 non-null float64 38 secondary_cleaner_state_floatbank2_b_air 13862 non-null float64 39 secondary_cleaner_state_floatbank2_b_level 13862 non-null float64 40 secondary_cleaner_state_floatbank3_a_air 13862 non-null float64 41 secondary_cleaner_state_floatbank3_a_level 13862 non-null float64 42 secondary_cleaner_state_floatbank3_b_air 13862 non-null float64 43 secondary_cleaner_state_floatbank3_b_level 13862 non-null float64 44 secondary_cleaner_state_floatbank4_a_air 13862 non-null float64 45 secondary_cleaner_state_floatbank4_a_level 13862 non-null float64 46 secondary_cleaner_state_floatbank4_b_air 13862 non-null float64 47 secondary_cleaner_state_floatbank4_b_level 13862 non-null float64 48 secondary_cleaner_state_floatbank5_a_air 13862 non-null float64 49 secondary_cleaner_state_floatbank5_a_level 13862 non-null float64 50 secondary_cleaner_state_floatbank5_b_air 13862 non-null float64 51 secondary_cleaner_state_floatbank5_b_level 13862 non-null float64 52 secondary_cleaner_state_floatbank6_a_air 13862 non-null float64 53 secondary_cleaner_state_floatbank6_a_level 13862 non-null float64 54 final_output_recovery 13862 non-null float64 dtypes: datetime64[ns](1), float64(54) memory usage: 5.8 MB
Также уберем и признак date
df_train = df_train.drop(['date'], axis=1)
#Проверяем
df_train.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 13862 entries, 0 to 13861 Data columns (total 54 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 rougher_input_feed_au 13862 non-null float64 1 rougher_input_feed_ag 13862 non-null float64 2 rougher_input_feed_pb 13862 non-null float64 3 rougher_input_feed_sol 13862 non-null float64 4 rougher_input_feed_rate 13862 non-null float64 5 rougher_input_feed_size 13862 non-null float64 6 rougher_input_floatbank10_sulfate 13862 non-null float64 7 rougher_input_floatbank10_xanthate 13862 non-null float64 8 rougher_state_floatbank10_a_air 13862 non-null float64 9 rougher_state_floatbank10_a_level 13862 non-null float64 10 rougher_state_floatbank10_b_air 13862 non-null float64 11 rougher_state_floatbank10_b_level 13862 non-null float64 12 rougher_state_floatbank10_c_air 13862 non-null float64 13 rougher_state_floatbank10_c_level 13862 non-null float64 14 rougher_state_floatbank10_d_air 13862 non-null float64 15 rougher_state_floatbank10_d_level 13862 non-null float64 16 rougher_state_floatbank10_e_air 13862 non-null float64 17 rougher_state_floatbank10_e_level 13862 non-null float64 18 rougher_state_floatbank10_f_air 13862 non-null float64 19 rougher_state_floatbank10_f_level 13862 non-null float64 20 rougher_input_floatbank11_sulfate 13862 non-null float64 21 rougher_input_floatbank11_xanthate 13862 non-null float64 22 rougher_output_recovery 13862 non-null float64 23 primary_cleaner_input_sulfate 13862 non-null float64 24 primary_cleaner_input_depressant 13862 non-null float64 25 primary_cleaner_input_feed_size 13862 non-null float64 26 primary_cleaner_input_xanthate 13862 non-null float64 27 primary_cleaner_state_floatbank8_a_air 13862 non-null float64 28 primary_cleaner_state_floatbank8_a_level 13862 non-null float64 29 primary_cleaner_state_floatbank8_b_air 13862 non-null float64 30 primary_cleaner_state_floatbank8_b_level 13862 non-null float64 31 primary_cleaner_state_floatbank8_c_air 13862 non-null float64 32 primary_cleaner_state_floatbank8_c_level 13862 non-null float64 33 primary_cleaner_state_floatbank8_d_air 13862 non-null float64 34 primary_cleaner_state_floatbank8_d_level 13862 non-null float64 35 secondary_cleaner_state_floatbank2_a_air 13862 non-null float64 36 secondary_cleaner_state_floatbank2_a_level 13862 non-null float64 37 secondary_cleaner_state_floatbank2_b_air 13862 non-null float64 38 secondary_cleaner_state_floatbank2_b_level 13862 non-null float64 39 secondary_cleaner_state_floatbank3_a_air 13862 non-null float64 40 secondary_cleaner_state_floatbank3_a_level 13862 non-null float64 41 secondary_cleaner_state_floatbank3_b_air 13862 non-null float64 42 secondary_cleaner_state_floatbank3_b_level 13862 non-null float64 43 secondary_cleaner_state_floatbank4_a_air 13862 non-null float64 44 secondary_cleaner_state_floatbank4_a_level 13862 non-null float64 45 secondary_cleaner_state_floatbank4_b_air 13862 non-null float64 46 secondary_cleaner_state_floatbank4_b_level 13862 non-null float64 47 secondary_cleaner_state_floatbank5_a_air 13862 non-null float64 48 secondary_cleaner_state_floatbank5_a_level 13862 non-null float64 49 secondary_cleaner_state_floatbank5_b_air 13862 non-null float64 50 secondary_cleaner_state_floatbank5_b_level 13862 non-null float64 51 secondary_cleaner_state_floatbank6_a_air 13862 non-null float64 52 secondary_cleaner_state_floatbank6_a_level 13862 non-null float64 53 final_output_recovery 13862 non-null float64 dtypes: float64(54) memory usage: 5.7 MB
Поделим признаки на общие и целевые. Из общих признаков для модели прогноза чернового концентрата уберем признаки первичной и вторичной очистки.
features_list = df_train.columns
list_to_cut = features_list[features_list.str.contains('primary|secondary|recovery')]
#Делим данные на общие и целевые признаки
features_rougher= df_train.drop(np.array(list_to_cut), axis=1)
features_final = df_train.drop(
['rougher_output_recovery','final_output_recovery'], axis=1)
target_rougher = df_train['rougher_output_recovery']
target_final = df_train['final_output_recovery']
%%time
best_depth_tree_r = 0
best_rougher_score_tree = 100
#Подбирая в цикле оптимальние гиперпараметры
for depth in range (1, 10, 1):
#Создаем и оцениваем модель для чернового концентрата
model_for_rougher = DecisionTreeRegressor(random_state = 123, max_depth = depth)
scores_rougher = cross_val_score(
model_for_rougher,
features_rougher,
target_rougher,
cv=5,
scoring=smape_score)
rougher_score = scores_rougher.mean()
if rougher_score < best_rougher_score_tree :
best_depth_tree_r = depth
best_rougher_score_tree = rougher_score
print(f'Ср. оценка качества для чернового концентрата = {best_rougher_score_tree:.2f}')
print( 'Максимальная глубина =', best_depth_tree_r)
Ср. оценка качества для чернового концентрата = 6.32 Максимальная глубина = 2 CPU times: total: 2.78 s Wall time: 5.22 s
%%time
best_depth_tree_f = 0
best_final_score_tree = 100
#Подбирая в цикле оптимальние гиперпараметры
for depth in range (1, 10, 1):
#Создаем и оцениваем модель для чернового концентрата
model_for_final = DecisionTreeRegressor(
random_state = 123, max_depth = depth)
scores_final = cross_val_score(
model_for_final,
features_final,
target_final,
cv=5,
scoring=smape_score)
final_score = scores_final.mean()
if final_score < best_final_score_tree :
best_depth_tree_f = depth
best_final_score_tree = final_score
print(f'Ср. оценка качества для итогового концентрата = {best_final_score_tree:.2f}')
print( 'Максимальная глубина =', best_depth_tree_f)
Ср. оценка качества для итогового концентрата = 8.91 Максимальная глубина = 2 CPU times: total: 6.88 s Wall time: 12.2 s
#Посчитаем итоговую sMAPE
result_tree = final_smape(best_rougher_score_tree, best_final_score_tree)
print (f'Итоговая sMAPE = {result_tree:.2f}')
Итоговая sMAPE = 8.27
df_train['final_output_recovery'].mean()
67.27423206558325
Для дерева решений неплохая итоговая метрика sMAPE = 8.27.
Посмотрим на модель линейной регрессии:
%%time
#Создаем и оцениваем модель для чернового концентрата
model_for_rougher = LinearRegression()
scores_rougher = cross_val_score(
model_for_rougher,
features_rougher,
target_rougher,
cv=5,
scoring=smape_score)
rougher_score = scores_rougher.mean()
print(f'Ср. оценка качества для чернового концентрата = {rougher_score:.2f}')
#Создаем и оцениваем модель для финального концентрата
model_for_final = LinearRegression()
scores_final = cross_val_score(
model_for_final,
features_final,
target_final,
cv=5,
scoring=smape_score)
final_score = scores_final.mean()
print(f'Ср. оценка качества для финального концентрата = {final_score:.2f}')
#Посчитаем итоговую sMAPE
print (f'Итоговая sMAPE = {final_smape(rougher_score, final_score):.2f}')
Ср. оценка качества для чернового концентрата = 6.56 Ср. оценка качества для финального концентрата = 9.40 Итоговая sMAPE = 8.69 CPU times: total: 734 ms Wall time: 251 ms
Для линейной регрессии итоговая метрика sMAPE уже немного хуже = 8.69.
Посмотрим на модель случайного леса:
%%time
best_est_forest_r = 0
best_depth_forest_r = 0
best_rougher_score_forest = 100
#Подбирая в цикле оптимальние гиперпараметры
for est in range(10, 60, 20):
for depth in range (1, 8, 2):
#Создаем и оцениваем модель для чернового концентрата
model_for_rougher = RandomForestRegressor(
random_state=123,
n_estimators=est,
max_depth=depth)
scores_rougher = cross_val_score(
model_for_rougher,
features_rougher,
target_rougher,
cv=5,
scoring=smape_score)
rougher_score = scores_rougher.mean()
if rougher_score < best_rougher_score_forest:
best_est_forest_r = est
best_depth_forest_r = depth
best_rougher_score_forest = rougher_score
print(f'Ср. оценка качества для чернового концентрата = \
{best_rougher_score_forest:.2f}')
print('Количество деревьев =', best_est_forest_r, \
'Максимальная глубина =', best_depth_forest_r)
Ср. оценка качества для чернового концентрата = 5.98 Количество деревьев = 10 Максимальная глубина = 3 CPU times: total: 47.3 s Wall time: 1min 44s
%%time
best_est_forest_f = 0
best_depth_forest_f = 0
best_final_score_forest = 100
#Подбирая в цикле оптимальние гиперпараметры
for est in range(10, 80, 20):
for depth in range (1, 8, 2):
#Создаем и оцениваем модель для финального концентрата
model_for_final = RandomForestRegressor(
random_state=123,
n_estimators=est,
max_depth=depth)
scores_final = cross_val_score(
model_for_final,
features_final,
target_final,
cv=5,
scoring=smape_score)
final_score = scores_final.mean()
if final_score < best_final_score_forest:
best_est_forest_f = est
best_depth_forest_f = depth
best_final_score_forest = final_score
print(f'Ср. оценка качества для финального концентрата =\
{best_final_score_forest:.2f}')
print('Количество деревьев =', best_est_forest_f, \
'Максимальная глубина =', best_depth_forest_f)
Ср. оценка качества для финального концентрата =9.01 Количество деревьев = 70 Максимальная глубина = 3 CPU times: total: 3min 53s Wall time: 7min 18s
#Посчитаем итоговую sMAPE
result_forest = final_smape(best_rougher_score_forest, best_final_score_forest)
print (f'Итоговая sMAPE = {result_forest:.2f}')
Итоговая sMAPE = 8.25
Лучше всего показала себя модель случайного леса (sMAPE = 8.25).
Значения метрик sMAPE для моделей:
| Модель | sMAPE на черновом концентрате | sMAPE на финальном концентрате | итоговая sMAPE |
|---|---|---|---|
| Дерево решений | 6.32 | 8.91 | 8.27 |
| Линейная регрессия | 6.56 | 9.40 | 8.69 |
| Случайный лес | 5.98 | 9.01 | 8.25 |
Дерево решений и случайный лес показывают очень хорошие результаты.
Но остановим свой выбор на наилучшей модели - модели случайного леса
features_list = df_test.columns
list_to_cut = features_list[
features_list.str.contains('primary|secondary|recovery|date')]
#Делим тестовые данные на общие и целевые признаки
features_rougher_test = df_test.drop(np.array(list_to_cut), axis=1)
features_final_test = df_test.drop(
['date', 'rougher_output_recovery','final_output_recovery'], axis=1)
target_rougher_test = df_test['rougher_output_recovery']
target_final_test = df_test['final_output_recovery']
#Наша выбранная модель с лучшими параметрами для чернового концентрата
model_for_rougher = RandomForestRegressor(
random_state=123, n_estimators=10, max_depth=3)
#Обучаем
model_for_rougher.fit(features_rougher,target_rougher)
#Делаем предсказание на тестовых данных
predicted_rougher = model_for_rougher.predict(features_rougher_test)
#Смотрим оценку
smape_on_rougher_test = smape(target_rougher_test, predicted_rougher)
print(f'Оценка качества модели для чернового концентрата =\
{smape_on_rougher_test:.2f}')
Оценка качества модели для чернового концентрата =4.36
#Наша модель случайного леса с лучшими параметрами для чернового концентрата
model_for_final = RandomForestRegressor(
random_state=123, n_estimators=70, max_depth=3)
#Обучаем
model_for_final.fit(features_final,target_final)
#Делаем предсказание на тестовых данных
predicted_final = model_for_final.predict(features_final_test)
#Смотрим оценку
smape_on_final_test = smape(target_final_test, predicted_final)
print(f'Оценка качества модели для финального концентрата = \
{smape_on_final_test:.2f}')
Оценка качества модели для финального концентрата = 8.02
print(f'Итоговая оценка качества модели на тестовых данных = \
{final_smape(smape_on_rougher_test , smape_on_final_test):.2f}')
Итоговая оценка качества модели на тестовых данных = 7.10
Хорошие результаты тестирования
Сравним показатели sMAPE с константной моделью, которая всегда предсказывает среднее значение:
#Создаем и обучаем константную модель для чернового концентрата
dummy_regr_for_rougher = DummyRegressor(strategy="mean")
dummy_regr_for_rougher.fit(features_rougher, target_rougher)
#Делаем предсказание на тестовых данных
dummy_predicted_rougher = dummy_regr_for_rougher.predict(
features_rougher_test)
#Смотрим оценку
smape_on_rougher = smape(target_rougher_test, dummy_predicted_rougher)
print(f'Оценка качества константной модели для чернового концентрата = \
{smape_on_rougher :.2f}')
Оценка качества константной модели для чернового концентрата = 6.10
#Создаем и обучаем константную модель для финального концентрата
dummy_regr_for_final = DummyRegressor(strategy="mean")
dummy_regr_for_final.fit(features_final, target_final)
#Делаем предсказание на тестовых данных
dummy_predicted_final = dummy_regr_for_final.predict(features_final_test)
#Смотрим оценку
smape_on_final = smape(target_final_test, dummy_predicted_final)
print(f'Оценка качества константной модели для чернового концентрата = \
{smape_on_final:.2f}')
Оценка качества константной модели для чернового концентрата = 9.26
print(f'Оценка качества константной модели итоговая = \
{final_smape(smape_on_rougher , smape_on_final):.2f}')
Оценка качества константной модели итоговая = 8.47
Метрика sMAPE у нашей модели существенно ниже чем у контантной, что говорит об ее адекватности.
Посмотрим какие факторы важны при прогнозировании содежания золота в черновом концентрате (топ 15 по убыванию):
#получим важность признаков
shap_values_rougher = shap.TreeExplainer(
model_for_rougher).shap_values(features_rougher)
#построим график
shap.summary_plot(
shap_values_rougher,
features_rougher,
plot_type="bar",
plot_size = (10,6),
color = '#5d89ba',
max_display = 15)
Самый большой вклад в прогноз вносят такие признаки как серебро в исходном сырье rounger_input_feed_ag, состояние флотационной установки жидкостью rougher_state_floatbank10_a_level и состояние флотационной установки воздухомrougher_state_floatbank10_c_air.
Некоторые признаки не имеют никакой важности для прогноза, их получается можно было бы даже удалить из датасета.
Дополнительно посмотрим положительные и отрицательные отношения признаков с целевой переменной:
#Цветовая карта для нашего графика
new_cmap = LinearSegmentedColormap.from_list("", ['#1976D2','#cb4154'])
shap.summary_plot(
shap_values_rougher,
features_rougher,
max_display=15,
cmap = new_cmap,
plot_size = (10,6));
Чем выше значение rounger_input_feed_ag или выше rougher_state_floatbank10_a_level, тем выше коэффициент содержания золота в черновом концентрате. Обратное утверждение также справедливо.
Чем меньше rougher_state_floatbank10_c_air, тем выше коэффициент содержания золота в черновом концентрате. Обратное утверждение также справедливо.
Посмотрим какие факторы важны при прогнозировании содежания золота в финальном концентрате (топ 15 по убыванию):
#получим важность признаков
shap_values_final = shap.TreeExplainer(
model_for_final).shap_values(features_final)
#построим график
shap.summary_plot(
shap_values_final,
features_final,
plot_type="bar",
plot_size = (10,6),
color = '#5d89ba',
max_display = 15)
Больше всего на прогноз содержания золота в финальном концентрате влияет серебро в исходном сырье rougher_input_feed_ag и значения сульфата на этапе первичной очистки
primary_cleaner_input_sulfate/
Некоторые признаки аналогично не имеют важности для прогноза, их получается можно было бы даже удалить из датасета.
Дополнительно посмотрим положительные и отрицательные отношения признаков с целевой переменной:
shap.summary_plot(
shap_values_final,
features_final,
max_display=15,
cmap = new_cmap,
plot_size = (10,6))
Чем выше значение rounger_input_feed_ag, тем выше коэффициент содержания золота в финальном концентрате, обратное утверждение также справедливо.
Если мало primary_cleaner_input_sulfate, то меньше коэффициент содержания золота в финальном концентрате, это справедливо только в одну сторону.
Нужно создать модель для предсказания коэффициента восстановления золота из золотосодержащей руды. Предоставлены данные с параметрами добычи и очистки.
Модель поможет оптимизировать производство, чтобы не запускать предприятие с убыточными характеристиками.
В обучающем датасете у нас содержится информация о 14579 наблюдениях по 87 признакам. В тестовом датасете у нас содержится информация о 4860 наблюдениях по 53 признакам. В общем датасете у нас содержится информация о 19439 наблюдениях по 87 признакам.
Признаков в тестовой выборке значительно меньше, скорее всего, часть признаков мы не можем знать в момент оценки и предсказаний. Поэтому те признаки, которых нет в тестовом датафрейме мы убрали из обучающего перед моделированием.
snapecase в названиях соблюден, все маленькими буквами. Все точки в названии были заменены на нижние подчеркивания.
С типами данных считаем что все в порядке, только тип данных для признака data заменили на datetime. Далее перед самим моделированием данный признак изымался из обучающего датафрейма, так как по логике он не должен оказывать влияния на коэффициент восстановления золота из руды.
В тестовый датафрейм из общего датафрейма были добавлены целевые признаки final.output.recovery и rougher.output.recovery
В обучающем датафрейме были обнаружены выбросы/аномальные данные в следующих признаках (из тех, что будут участвовать в моделировании):
rougher_input_feed_size
rougher_state_floatbank10_a_air
rougher_state_floatbank10_f_air
rougher_state_floatbank10_e_air
Аномальные данные в данных признаках были обрезаны (заменены на пустые знгачения) по 0.4 процентиля слева и справа.
В тестовом датафрейме выбросов и аномальных данных можно сказать нет.
В общем датафрейме похоже на выбросы или наличие аномальных данных в следующих признаках:
rougher_input_feed_size
rougher_state_floatbank10_a_air
rougher_state_floatbank10_f_air
rougher_state_floatbank10_e_air
'rougher_calculation_sulfate_to_au_concentrate'
rougher_calculation_floatbank10_sulfate_to_au_feed
rougher_calculation_floatbank11_sulfate_to_au_feed
rougher_calculation_au_pb_ratio
secondary_cleaner_output_tail_au
final_output_concentrate_au
Общий датафрейм оставили без изменений.
Была проведена проверка, что эффективность обогащения рассчитана правильно для признака rougher_output_recovery. Была посчитана метрика MAE между вычисленной эффективностью и имеющейся. Ответы совпадают с точностью до 13 знака после запятой, что говорит о том, что эффективность обогащения рассчитана верно.
Во всех датафреймах были обнаружены пропуски в данных. В обучающем и тестовом датафрейме пропуски были удалены с помощью метода ffill
Дубликатов наблюдений в датафреймах не обнаружено.
Видно как концентрация золота существенно увеличивается после каждого этапа.
Концентрация серебра увеличивается в черновом концентрате до самых своих больших значений, и далее падает.
Концентрация свинца увеличивается в черновом концентрате и после первой очистки, и следующие манипуляции не сильно изменяют эту концентрацию, в финале она остается примерно такой же как и после первой очистки.
На графиках заметны еще некоторые аномалии - значения концентраций около нуля. Они конечно уменьшаются на каждом этапе, но особенно они выглядят аномальными и странными для финального концентрата, что похоже на ошибки измерений.
Было проанализировано есть ли в обучающем датафрейме те данные, для которых финальная концентрация нулевая на примере золота. Было обнаружено и удалено 98 строк для которых финальная концентрация металлов нулевая. (Причем эта аномалия в наблюдениях были сразу для всех металлов)
Была исследована суммарная концентрация металлов на разных стадиях: в сырье, в черновом концентрате, в концентрате после первой очистки и в финальном концентрате:
На графике видно что общая концентрация металлов растет после каждого следующего этапа, а разброс уменьшается, что соответствует здравому смыслу.
Также заметно, что есть нулевые и околонулевые значения суммарных концентрацией. Скорее всего это связано со сбоем измерительного оборудования. Такие наблюдения в кол-ве 619 строк были удалены из обучающих данных
Распределение размеров гранул исходного сырья очень похожи, для моделирования нас такая картина устраивает.
Модели строились для предсказания двух параметров:
* эффективность обогащения чернового концентрата rougher_output_recovery;
* эффективность обогащения финального концентрата final_output_recovery.
Для оценки использовалась пользовательская метрика sMAPE.
Было рассмотрено три регрессионных модели, оценка качества производилась с помощью кросс валидации. Получены следующие результаты:
| Модель | sMAPE на черновом концентрате | sMAPE на финальном концентрате | итоговая sMAPE |
|---|---|---|---|
| Дерево решений | 6.32 | 8.91 | 8.27 |
| Линейная регрессия | 6.56 | 9.40 | 8.69 |
| Случайный лес | 5.98 | 9.01 | 8.25 |
Дерево решений и случайный лес показывают очень хорошие результаты.
Но остановим свой выбор на наилучшей модели - модели случайного леса. Лучшие параметры модели:
* для чернового этапа количество деревьев = 10, максимальная глубина = 3.
* для финального этапа количество деревьев = 70, Максимальная глубина = 3.
Для проверки на адекватность использовалась константная модель, предсказывающая среднее значение целевого признака.
По результатам тестирования и проверки на адекватность были получены следующие результаты:
| Модель | sMAPE на черновом концентрате | sMAPE на финальном концентрате | итоговая sMAPE |
|---|---|---|---|
| Случайный лес | 4.36 | 8.02 | 7.10 |
| Константная модель | 6.10 | 9.26 | 8.47 |
На тестовых данных модель показала хорошее качество.
Метрика sMAPE у нашей модели существенно ниже чем у контантной, что говорит об ее адекватности.
Важность факторов при прогнозировании коэффициента восстановления золота (10 с самой большой важностью):
Самый большой вклад в прогноз вносят такие признаки как серебро в исходном сырье rounger_input_feed_ag, состояние флотационной установки жидкостью rougher_state_floatbank10_a_level и состояние флотационной установки воздухомrougher_state_floatbank10_c_air.
Некоторые признаки не имеют никакой важности для прогноза, их получается можно было бы даже удалить из датасета.
Дополнительно были выведены на график положительные и отрицательные отношения признаков с целевой переменной:
Чем выше значение rounger_input_feed_ag или выше rougher_state_floatbank10_a_level, тем выше коэффициент содержания золота в черновом концентрате. Обратное утверждение также справедливо.
Чем меньше rougher_state_floatbank10_c_air, тем выше коэффициент содержания золота в черновом концентрате. Обратное утверждение также справедливо.
Больше всего на прогноз содержания золота в финальном концентрате влияет серебро в исходном сырье rougher_input_feed_ag и значения сульфата на этапе первичной очистки
primary_cleaner_input_sulfate/
Некоторые признаки аналогично не имеют важности для прогноза, их получается можно было бы даже удалить из датасета.
Дополнительно были выведены на график положительные и отрицательные отношения признаков с целевой переменной:
Чем выше значение rounger_input_feed_ag, тем выше коэффициент содержания золота в финальном концентрате, обратное утверждение также справедливо.
Если мало primary_cleaner_input_sulfate, то меньше коэффициент содержания золота в финальном концентрате, это справедливо только в одну сторону.
По итогу работы можно рекомендовать модель случайного леса, с указанными выше характеристиками для предсказания коэффициента восстановления золота из золотосодержащей руды.
Мы надеемся, что данная модель поможет оптимизировать работу предприятия, и готовы провести дополнительное исследование, если текущий результат недостаточен для качественной оценки производственного процесса.